Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
config DM_PERSISTENT_DATA
tristate
depends on BLK_DEV_DM
select CRC32
select DM_BUFIO
help
Library providing immutable on-disk data structure support for
device-mapper targets such as the thin provisioning target.
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_DM_PERSISTENT_DATA) += dm-persistent-data.o
dm-persistent-data-objs := \
dm-array.o \
dm-bitset.o \
dm-block-manager.o \
dm-space-map-common.o \
dm-space-map-disk.o \
dm-space-map-metadata.o \
dm-transaction-manager.o \
dm-btree.o \
dm-btree-remove.o \
dm-btree-spine.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,220 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2012 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_ARRAY_H
#define _LINUX_DM_ARRAY_H
#include "dm-btree.h"
/*----------------------------------------------------------------*/
/*
* The dm-array is a persistent version of an array. It packs the data
* more efficiently than a btree which will result in less disk space use,
* and a performance boost. The element get and set operations are still
* O(ln(n)), but with a much smaller constant.
*
* The value type structure is reused from the btree type to support proper
* reference counting of values.
*
* The arrays implicitly know their length, and bounds are checked for
* lookups and updated. It doesn't store this in an accessible place
* because it would waste a whole metadata block. Make sure you store the
* size along with the array root in your encompassing data.
*
* Array entries are indexed via an unsigned integer starting from zero.
* Arrays are not sparse; if you resize an array to have 'n' entries then
* 'n - 1' will be the last valid index.
*
* Typical use:
*
* a) initialise a dm_array_info structure. This describes the array
* values and ties it into a specific transaction manager. It holds no
* instance data; the same info can be used for many similar arrays if
* you wish.
*
* 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 array. 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 array with dm_array_empty().
*
* Like the other data structures in this library, dm_array 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.
*
* c) resize an array with dm_array_resize().
*
* d) Get a value from the array with dm_array_get_value().
*
* e) Set a value in the array with dm_array_set_value().
*
* f) Walk an array of values in index order with dm_array_walk(). More
* efficient than making many calls to dm_array_get_value().
*
* g) Destroy the array with dm_array_del(). This tells the transaction
* manager that you're no longer using this data structure so it can
* recycle it's blocks. (dm_array_dec() would be a better name for it,
* but del is in keeping with dm_btree_del()).
*/
/*
* Describes an array. Don't initialise this structure yourself, use the
* init function below.
*/
struct dm_array_info {
struct dm_transaction_manager *tm;
struct dm_btree_value_type value_type;
struct dm_btree_info btree_info;
};
/*
* Sets up a dm_array_info structure. You don't need to do anything with
* this structure when you finish using it.
*
* info - the structure being filled in.
* tm - the transaction manager that should supervise this structure.
* vt - describes the leaf values.
*/
void dm_array_info_init(struct dm_array_info *info,
struct dm_transaction_manager *tm,
struct dm_btree_value_type *vt);
/*
* Create an empty, zero length array.
*
* info - describes the array
* root - on success this will be filled out with the root block
*/
int dm_array_empty(struct dm_array_info *info, dm_block_t *root);
/*
* Resizes the array.
*
* info - describes the array
* root - the root block of the array on disk
* old_size - the caller is responsible for remembering the size of
* the array
* new_size - can be bigger or smaller than old_size
* value - if we're growing the array the new entries will have this value
* new_root - on success, points to the new root block
*
* If growing the inc function for 'value' will be called the appropriate
* number of times. So if the caller is holding a reference they may want
* to drop it.
*/
int dm_array_resize(struct dm_array_info *info, dm_block_t root,
uint32_t old_size, uint32_t new_size,
const void *value, dm_block_t *new_root)
__dm_written_to_disk(value);
/*
* Creates a new array populated with values provided by a callback
* function. This is more efficient than creating an empty array,
* resizing, and then setting values since that process incurs a lot of
* copying.
*
* Assumes 32bit values for now since it's only used by the cache hint
* array.
*
* info - describes the array
* root - the root block of the array on disk
* size - the number of entries in the array
* fn - the callback
* context - passed to the callback
*/
typedef int (*value_fn)(uint32_t index, void *value_le, void *context);
int dm_array_new(struct dm_array_info *info, dm_block_t *root,
uint32_t size, value_fn fn, void *context);
/*
* Frees a whole array. The value_type's decrement operation will be called
* for all values in the array
*/
int dm_array_del(struct dm_array_info *info, dm_block_t root);
/*
* Lookup a value in the array
*
* info - describes the array
* root - root block of the array
* index - array index
* value - the value to be read. Will be in on-disk format of course.
*
* -ENODATA will be returned if the index is out of bounds.
*/
int dm_array_get_value(struct dm_array_info *info, dm_block_t root,
uint32_t index, void *value);
/*
* Set an entry in the array.
*
* info - describes the array
* root - root block of the array
* index - array index
* value - value to be written to disk. Make sure you confirm the value is
* in on-disk format with__dm_bless_for_disk() before calling.
* new_root - the new root block
*
* The old value being overwritten will be decremented, the new value
* incremented.
*
* -ENODATA will be returned if the index is out of bounds.
*/
int dm_array_set_value(struct dm_array_info *info, dm_block_t root,
uint32_t index, const void *value, dm_block_t *new_root)
__dm_written_to_disk(value);
/*
* Walk through all the entries in an array.
*
* info - describes the array
* root - root block of the array
* fn - called back for every element
* context - passed to the callback
*/
int dm_array_walk(struct dm_array_info *info, dm_block_t root,
int (*fn)(void *context, uint64_t key, void *leaf),
void *context);
/*----------------------------------------------------------------*/
/*
* Cursor api.
*
* This lets you iterate through all the entries in an array efficiently
* (it will preload metadata).
*
* I'm using a cursor, rather than a walk function with a callback because
* the cache target needs to iterate both the mapping and hint arrays in
* unison.
*/
struct dm_array_cursor {
struct dm_array_info *info;
struct dm_btree_cursor cursor;
struct dm_block *block;
struct array_block *ab;
unsigned int index;
};
int dm_array_cursor_begin(struct dm_array_info *info,
dm_block_t root, struct dm_array_cursor *c);
void dm_array_cursor_end(struct dm_array_cursor *c);
uint32_t dm_array_cursor_index(struct dm_array_cursor *c);
int dm_array_cursor_next(struct dm_array_cursor *c);
int dm_array_cursor_skip(struct dm_array_cursor *c, uint32_t count);
/*
* value_le is only valid while the cursor points at the current value.
*/
void dm_array_cursor_get_value(struct dm_array_cursor *c, void **value_le);
/*----------------------------------------------------------------*/
#endif /* _LINUX_DM_ARRAY_H */
@@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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);
struct packer_context {
bit_value_fn fn;
unsigned int nr_bits;
void *context;
};
static int pack_bits(uint32_t index, void *value, void *context)
{
int r;
struct packer_context *p = context;
unsigned int bit, nr = min(64u, p->nr_bits - (index * 64));
uint64_t word = 0;
bool bv;
for (bit = 0; bit < nr; bit++) {
r = p->fn(index * 64 + bit, &bv, p->context);
if (r)
return r;
if (bv)
set_bit(bit, (unsigned long *) &word);
else
clear_bit(bit, (unsigned long *) &word);
}
*((__le64 *) value) = cpu_to_le64(word);
return 0;
}
int dm_bitset_new(struct dm_disk_bitset *info, dm_block_t *root,
uint32_t size, bit_value_fn fn, void *context)
{
struct packer_context p;
p.fn = fn;
p.nr_bits = size;
p.context = context;
return dm_array_new(&info->array_info, root, dm_div_up(size, 64), pack_bits, &p);
}
EXPORT_SYMBOL_GPL(dm_bitset_new);
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 || !info->dirty)
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;
info->dirty = 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;
info->dirty = false;
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 int 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 int 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);
info->dirty = true;
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 int 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);
info->dirty = true;
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 int 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);
static int cursor_next_array_entry(struct dm_bitset_cursor *c)
{
int r;
__le64 *value;
r = dm_array_cursor_next(&c->cursor);
if (r)
return r;
dm_array_cursor_get_value(&c->cursor, (void **) &value);
c->array_index++;
c->bit_index = 0;
c->current_bits = le64_to_cpu(*value);
return 0;
}
int dm_bitset_cursor_begin(struct dm_disk_bitset *info,
dm_block_t root, uint32_t nr_entries,
struct dm_bitset_cursor *c)
{
int r;
__le64 *value;
if (!nr_entries)
return -ENODATA;
c->info = info;
c->entries_remaining = nr_entries;
r = dm_array_cursor_begin(&info->array_info, root, &c->cursor);
if (r)
return r;
dm_array_cursor_get_value(&c->cursor, (void **) &value);
c->array_index = 0;
c->bit_index = 0;
c->current_bits = le64_to_cpu(*value);
return r;
}
EXPORT_SYMBOL_GPL(dm_bitset_cursor_begin);
void dm_bitset_cursor_end(struct dm_bitset_cursor *c)
{
return dm_array_cursor_end(&c->cursor);
}
EXPORT_SYMBOL_GPL(dm_bitset_cursor_end);
int dm_bitset_cursor_next(struct dm_bitset_cursor *c)
{
int r = 0;
if (!c->entries_remaining)
return -ENODATA;
c->entries_remaining--;
if (++c->bit_index > 63)
r = cursor_next_array_entry(c);
return r;
}
EXPORT_SYMBOL_GPL(dm_bitset_cursor_next);
int dm_bitset_cursor_skip(struct dm_bitset_cursor *c, uint32_t count)
{
int r;
__le64 *value;
uint32_t nr_array_skip;
uint32_t remaining_in_word = 64 - c->bit_index;
if (c->entries_remaining < count)
return -ENODATA;
if (count < remaining_in_word) {
c->bit_index += count;
c->entries_remaining -= count;
return 0;
} else {
c->entries_remaining -= remaining_in_word;
count -= remaining_in_word;
}
nr_array_skip = (count / 64) + 1;
r = dm_array_cursor_skip(&c->cursor, nr_array_skip);
if (r)
return r;
dm_array_cursor_get_value(&c->cursor, (void **) &value);
c->entries_remaining -= count;
c->array_index += nr_array_skip;
c->bit_index = count & 63;
c->current_bits = le64_to_cpu(*value);
return 0;
}
EXPORT_SYMBOL_GPL(dm_bitset_cursor_skip);
bool dm_bitset_cursor_get_value(struct dm_bitset_cursor *c)
{
return test_bit(c->bit_index, (unsigned long *) &c->current_bits);
}
EXPORT_SYMBOL_GPL(dm_bitset_cursor_get_value);
/*----------------------------------------------------------------*/
@@ -0,0 +1,206 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 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;
bool dirty: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);
/*
* Creates a new bitset populated with values provided by a callback
* function. This is more efficient than creating an empty bitset,
* resizing, and then setting values since that process incurs a lot of
* copying.
*
* info - describes the array
* root - the root block of the array on disk
* size - the number of entries in the array
* fn - the callback
* context - passed to the callback
*/
typedef int (*bit_value_fn)(uint32_t index, bool *value, void *context);
int dm_bitset_new(struct dm_disk_bitset *info, dm_block_t *root,
uint32_t size, bit_value_fn fn, void *context);
/*
* 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);
struct dm_bitset_cursor {
struct dm_disk_bitset *info;
struct dm_array_cursor cursor;
uint32_t entries_remaining;
uint32_t array_index;
uint32_t bit_index;
uint64_t current_bits;
};
/*
* Make sure you've flush any dm_disk_bitset and updated the root before
* using this.
*/
int dm_bitset_cursor_begin(struct dm_disk_bitset *info,
dm_block_t root, uint32_t nr_entries,
struct dm_bitset_cursor *c);
void dm_bitset_cursor_end(struct dm_bitset_cursor *c);
int dm_bitset_cursor_next(struct dm_bitset_cursor *c);
int dm_bitset_cursor_skip(struct dm_bitset_cursor *c, uint32_t count);
bool dm_bitset_cursor_get_value(struct dm_bitset_cursor *c);
/*----------------------------------------------------------------*/
#endif /* _LINUX_DM_BITSET_H */
@@ -0,0 +1,662 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-block-manager.h"
#include "dm-persistent-data-internal.h"
#include <linux/dm-bufio.h>
#include <linux/crc32c.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/rwsem.h>
#include <linux/device-mapper.h>
#include <linux/stacktrace.h>
#include <linux/sched/task.h>
#define DM_MSG_PREFIX "block manager"
/*----------------------------------------------------------------*/
#ifdef CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING
/*
* This is a read/write semaphore with a couple of differences.
*
* i) There is a restriction on the number of concurrent read locks that
* may be held at once. This is just an implementation detail.
*
* ii) Recursive locking attempts are detected and return EINVAL. A stack
* trace is also emitted for the previous lock acquisition.
*
* iii) Priority is given to write locks.
*/
#define MAX_HOLDERS 4
#define MAX_STACK 10
struct stack_store {
unsigned int nr_entries;
unsigned long entries[MAX_STACK];
};
struct block_lock {
spinlock_t lock;
__s32 count;
struct list_head waiters;
struct task_struct *holders[MAX_HOLDERS];
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
struct stack_store traces[MAX_HOLDERS];
#endif
};
struct waiter {
struct list_head list;
struct task_struct *task;
int wants_write;
};
static unsigned int __find_holder(struct block_lock *lock,
struct task_struct *task)
{
unsigned int i;
for (i = 0; i < MAX_HOLDERS; i++)
if (lock->holders[i] == task)
break;
BUG_ON(i == MAX_HOLDERS);
return i;
}
/* call this *after* you increment lock->count */
static void __add_holder(struct block_lock *lock, struct task_struct *task)
{
unsigned int h = __find_holder(lock, NULL);
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
struct stack_store *t;
#endif
get_task_struct(task);
lock->holders[h] = task;
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
t = lock->traces + h;
t->nr_entries = stack_trace_save(t->entries, MAX_STACK, 2);
#endif
}
/* call this *before* you decrement lock->count */
static void __del_holder(struct block_lock *lock, struct task_struct *task)
{
unsigned int h = __find_holder(lock, task);
lock->holders[h] = NULL;
put_task_struct(task);
}
static int __check_holder(struct block_lock *lock)
{
unsigned int i;
for (i = 0; i < MAX_HOLDERS; i++) {
if (lock->holders[i] == current) {
DMERR("recursive lock detected in metadata");
#ifdef CONFIG_DM_DEBUG_BLOCK_STACK_TRACING
DMERR("previously held here:");
stack_trace_print(lock->traces[i].entries,
lock->traces[i].nr_entries, 4);
DMERR("subsequent acquisition attempted here:");
dump_stack();
#endif
return -EINVAL;
}
}
return 0;
}
static void __wait(struct waiter *w)
{
for (;;) {
set_current_state(TASK_UNINTERRUPTIBLE);
if (!w->task)
break;
schedule();
}
set_current_state(TASK_RUNNING);
}
static void __wake_waiter(struct waiter *w)
{
struct task_struct *task;
list_del(&w->list);
task = w->task;
smp_mb();
w->task = NULL;
wake_up_process(task);
}
/*
* We either wake a few readers or a single writer.
*/
static void __wake_many(struct block_lock *lock)
{
struct waiter *w, *tmp;
BUG_ON(lock->count < 0);
list_for_each_entry_safe(w, tmp, &lock->waiters, list) {
if (lock->count >= MAX_HOLDERS)
return;
if (w->wants_write) {
if (lock->count > 0)
return; /* still read locked */
lock->count = -1;
__add_holder(lock, w->task);
__wake_waiter(w);
return;
}
lock->count++;
__add_holder(lock, w->task);
__wake_waiter(w);
}
}
static void bl_init(struct block_lock *lock)
{
int i;
spin_lock_init(&lock->lock);
lock->count = 0;
INIT_LIST_HEAD(&lock->waiters);
for (i = 0; i < MAX_HOLDERS; i++)
lock->holders[i] = NULL;
}
static int __available_for_read(struct block_lock *lock)
{
return lock->count >= 0 &&
lock->count < MAX_HOLDERS &&
list_empty(&lock->waiters);
}
static int bl_down_read(struct block_lock *lock)
{
int r;
struct waiter w;
spin_lock(&lock->lock);
r = __check_holder(lock);
if (r) {
spin_unlock(&lock->lock);
return r;
}
if (__available_for_read(lock)) {
lock->count++;
__add_holder(lock, current);
spin_unlock(&lock->lock);
return 0;
}
get_task_struct(current);
w.task = current;
w.wants_write = 0;
list_add_tail(&w.list, &lock->waiters);
spin_unlock(&lock->lock);
__wait(&w);
put_task_struct(current);
return 0;
}
static int bl_down_read_nonblock(struct block_lock *lock)
{
int r;
spin_lock(&lock->lock);
r = __check_holder(lock);
if (r)
goto out;
if (__available_for_read(lock)) {
lock->count++;
__add_holder(lock, current);
r = 0;
} else
r = -EWOULDBLOCK;
out:
spin_unlock(&lock->lock);
return r;
}
static void bl_up_read(struct block_lock *lock)
{
spin_lock(&lock->lock);
BUG_ON(lock->count <= 0);
__del_holder(lock, current);
--lock->count;
if (!list_empty(&lock->waiters))
__wake_many(lock);
spin_unlock(&lock->lock);
}
static int bl_down_write(struct block_lock *lock)
{
int r;
struct waiter w;
spin_lock(&lock->lock);
r = __check_holder(lock);
if (r) {
spin_unlock(&lock->lock);
return r;
}
if (lock->count == 0 && list_empty(&lock->waiters)) {
lock->count = -1;
__add_holder(lock, current);
spin_unlock(&lock->lock);
return 0;
}
get_task_struct(current);
w.task = current;
w.wants_write = 1;
/*
* Writers given priority. We know there's only one mutator in the
* system, so ignoring the ordering reversal.
*/
list_add(&w.list, &lock->waiters);
spin_unlock(&lock->lock);
__wait(&w);
put_task_struct(current);
return 0;
}
static void bl_up_write(struct block_lock *lock)
{
spin_lock(&lock->lock);
__del_holder(lock, current);
lock->count = 0;
if (!list_empty(&lock->waiters))
__wake_many(lock);
spin_unlock(&lock->lock);
}
static void report_recursive_bug(dm_block_t b, int r)
{
if (r == -EINVAL)
DMERR("recursive acquisition of block %llu requested.",
(unsigned long long) b);
}
#else /* !CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING */
#define bl_init(x) do { } while (0)
#define bl_down_read(x) 0
#define bl_down_read_nonblock(x) 0
#define bl_up_read(x) do { } while (0)
#define bl_down_write(x) 0
#define bl_up_write(x) do { } while (0)
#define report_recursive_bug(x, y) do { } while (0)
#endif /* CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING */
/*----------------------------------------------------------------*/
/*
* Block manager is currently implemented using dm-bufio. struct
* dm_block_manager and struct dm_block map directly onto a couple of
* structs in the bufio interface. I want to retain the freedom to move
* away from bufio in the future. So these structs are just cast within
* this .c file, rather than making it through to the public interface.
*/
static struct dm_buffer *to_buffer(struct dm_block *b)
{
return (struct dm_buffer *) b;
}
dm_block_t dm_block_location(struct dm_block *b)
{
return dm_bufio_get_block_number(to_buffer(b));
}
EXPORT_SYMBOL_GPL(dm_block_location);
void *dm_block_data(struct dm_block *b)
{
return dm_bufio_get_block_data(to_buffer(b));
}
EXPORT_SYMBOL_GPL(dm_block_data);
struct buffer_aux {
const struct dm_block_validator *validator;
int write_locked;
#ifdef CONFIG_DM_DEBUG_BLOCK_MANAGER_LOCKING
struct block_lock lock;
#endif
};
static void dm_block_manager_alloc_callback(struct dm_buffer *buf)
{
struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
aux->validator = NULL;
bl_init(&aux->lock);
}
static void dm_block_manager_write_callback(struct dm_buffer *buf)
{
struct buffer_aux *aux = dm_bufio_get_aux_data(buf);
if (aux->validator) {
aux->validator->prepare_for_write(aux->validator, (struct dm_block *) buf,
dm_bufio_get_block_size(dm_bufio_get_client(buf)));
}
}
/*
* -------------------------------------------------------------
* Public interface
*--------------------------------------------------------------
*/
struct dm_block_manager {
struct dm_bufio_client *bufio;
bool read_only:1;
};
struct dm_block_manager *dm_block_manager_create(struct block_device *bdev,
unsigned int block_size,
unsigned int max_held_per_thread)
{
int r;
struct dm_block_manager *bm;
bm = kmalloc_obj(*bm);
if (!bm) {
r = -ENOMEM;
goto bad;
}
bm->bufio = dm_bufio_client_create(bdev, block_size, max_held_per_thread,
sizeof(struct buffer_aux),
dm_block_manager_alloc_callback,
dm_block_manager_write_callback,
0);
if (IS_ERR(bm->bufio)) {
r = PTR_ERR(bm->bufio);
kfree(bm);
goto bad;
}
bm->read_only = false;
return bm;
bad:
return ERR_PTR(r);
}
EXPORT_SYMBOL_GPL(dm_block_manager_create);
void dm_block_manager_destroy(struct dm_block_manager *bm)
{
dm_bufio_client_destroy(bm->bufio);
kfree(bm);
}
EXPORT_SYMBOL_GPL(dm_block_manager_destroy);
void dm_block_manager_reset(struct dm_block_manager *bm)
{
dm_bufio_client_reset(bm->bufio);
}
EXPORT_SYMBOL_GPL(dm_block_manager_reset);
unsigned int dm_bm_block_size(struct dm_block_manager *bm)
{
return dm_bufio_get_block_size(bm->bufio);
}
EXPORT_SYMBOL_GPL(dm_bm_block_size);
dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm)
{
return dm_bufio_get_device_size(bm->bufio);
}
static int dm_bm_validate_buffer(struct dm_block_manager *bm,
struct dm_buffer *buf,
struct buffer_aux *aux,
const struct dm_block_validator *v)
{
if (unlikely(!aux->validator)) {
int r;
if (!v)
return 0;
r = v->check(v, (struct dm_block *) buf, dm_bufio_get_block_size(bm->bufio));
if (unlikely(r)) {
DMERR_LIMIT("%s validator check failed for block %llu", v->name,
(unsigned long long) dm_bufio_get_block_number(buf));
return r;
}
aux->validator = v;
} else {
if (unlikely(aux->validator != v)) {
DMERR_LIMIT("validator mismatch (old=%s vs new=%s) for block %llu",
aux->validator->name, v ? v->name : "NULL",
(unsigned long long) dm_bufio_get_block_number(buf));
return -EINVAL;
}
}
return 0;
}
int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result)
{
struct buffer_aux *aux;
void *p;
int r;
p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
if (IS_ERR(p))
return PTR_ERR(p);
aux = dm_bufio_get_aux_data(to_buffer(*result));
r = bl_down_read(&aux->lock);
if (unlikely(r)) {
dm_bufio_release(to_buffer(*result));
report_recursive_bug(b, r);
return r;
}
aux->write_locked = 0;
r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
if (unlikely(r)) {
bl_up_read(&aux->lock);
dm_bufio_release(to_buffer(*result));
return r;
}
return 0;
}
EXPORT_SYMBOL_GPL(dm_bm_read_lock);
int dm_bm_write_lock(struct dm_block_manager *bm,
dm_block_t b, const struct dm_block_validator *v,
struct dm_block **result)
{
struct buffer_aux *aux;
void *p;
int r;
if (dm_bm_is_read_only(bm))
return -EPERM;
p = dm_bufio_read(bm->bufio, b, (struct dm_buffer **) result);
if (IS_ERR(p))
return PTR_ERR(p);
aux = dm_bufio_get_aux_data(to_buffer(*result));
r = bl_down_write(&aux->lock);
if (r) {
dm_bufio_release(to_buffer(*result));
report_recursive_bug(b, r);
return r;
}
aux->write_locked = 1;
r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
if (unlikely(r)) {
bl_up_write(&aux->lock);
dm_bufio_release(to_buffer(*result));
return r;
}
return 0;
}
EXPORT_SYMBOL_GPL(dm_bm_write_lock);
int dm_bm_read_try_lock(struct dm_block_manager *bm,
dm_block_t b, const struct dm_block_validator *v,
struct dm_block **result)
{
struct buffer_aux *aux;
void *p;
int r;
p = dm_bufio_get(bm->bufio, b, (struct dm_buffer **) result);
if (IS_ERR(p))
return PTR_ERR(p);
if (unlikely(!p))
return -EWOULDBLOCK;
aux = dm_bufio_get_aux_data(to_buffer(*result));
r = bl_down_read_nonblock(&aux->lock);
if (r < 0) {
dm_bufio_release(to_buffer(*result));
report_recursive_bug(b, r);
return r;
}
aux->write_locked = 0;
r = dm_bm_validate_buffer(bm, to_buffer(*result), aux, v);
if (unlikely(r)) {
bl_up_read(&aux->lock);
dm_bufio_release(to_buffer(*result));
return r;
}
return 0;
}
int dm_bm_write_lock_zero(struct dm_block_manager *bm,
dm_block_t b, const struct dm_block_validator *v,
struct dm_block **result)
{
int r;
struct buffer_aux *aux;
void *p;
if (dm_bm_is_read_only(bm))
return -EPERM;
p = dm_bufio_new(bm->bufio, b, (struct dm_buffer **) result);
if (IS_ERR(p))
return PTR_ERR(p);
memset(p, 0, dm_bm_block_size(bm));
aux = dm_bufio_get_aux_data(to_buffer(*result));
r = bl_down_write(&aux->lock);
if (r) {
dm_bufio_release(to_buffer(*result));
return r;
}
aux->write_locked = 1;
aux->validator = v;
return 0;
}
EXPORT_SYMBOL_GPL(dm_bm_write_lock_zero);
void dm_bm_unlock(struct dm_block *b)
{
struct buffer_aux *aux = dm_bufio_get_aux_data(to_buffer(b));
if (aux->write_locked) {
dm_bufio_mark_buffer_dirty(to_buffer(b));
bl_up_write(&aux->lock);
} else
bl_up_read(&aux->lock);
dm_bufio_release(to_buffer(b));
}
EXPORT_SYMBOL_GPL(dm_bm_unlock);
int dm_bm_flush(struct dm_block_manager *bm)
{
if (dm_bm_is_read_only(bm))
return -EPERM;
return dm_bufio_write_dirty_buffers(bm->bufio);
}
EXPORT_SYMBOL_GPL(dm_bm_flush);
void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b)
{
dm_bufio_prefetch(bm->bufio, b, 1);
}
bool dm_bm_is_read_only(struct dm_block_manager *bm)
{
return bm ? bm->read_only : true;
}
EXPORT_SYMBOL_GPL(dm_bm_is_read_only);
void dm_bm_set_read_only(struct dm_block_manager *bm)
{
if (bm)
bm->read_only = true;
}
EXPORT_SYMBOL_GPL(dm_bm_set_read_only);
void dm_bm_set_read_write(struct dm_block_manager *bm)
{
if (bm)
bm->read_only = false;
}
EXPORT_SYMBOL_GPL(dm_bm_set_read_write);
u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor)
{
return crc32c(~(u32) 0, data, len) ^ init_xor;
}
EXPORT_SYMBOL_GPL(dm_bm_checksum);
/*----------------------------------------------------------------*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Joe Thornber <dm-devel@lists.linux.dev>");
MODULE_DESCRIPTION("Immutable metadata library for dm");
/*----------------------------------------------------------------*/
@@ -0,0 +1,138 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_BLOCK_MANAGER_H
#define _LINUX_DM_BLOCK_MANAGER_H
#include <linux/types.h>
#include <linux/blkdev.h>
/*----------------------------------------------------------------*/
/*
* Block number.
*/
typedef uint64_t dm_block_t;
struct dm_block;
dm_block_t dm_block_location(struct dm_block *b);
void *dm_block_data(struct dm_block *b);
/*----------------------------------------------------------------*/
/*
* @name should be a unique identifier for the block manager, no longer
* than 32 chars.
*
* @max_held_per_thread should be the maximum number of locks, read or
* write, that an individual thread holds at any one time.
*/
struct dm_block_manager;
struct dm_block_manager *dm_block_manager_create(
struct block_device *bdev, unsigned int block_size,
unsigned int max_held_per_thread);
void dm_block_manager_destroy(struct dm_block_manager *bm);
void dm_block_manager_reset(struct dm_block_manager *bm);
unsigned int dm_bm_block_size(struct dm_block_manager *bm);
dm_block_t dm_bm_nr_blocks(struct dm_block_manager *bm);
/*----------------------------------------------------------------*/
/*
* The validator allows the caller to verify newly-read data and modify
* the data just before writing, e.g. to calculate checksums. It's
* important to be consistent with your use of validators. The only time
* you can change validators is if you call dm_bm_write_lock_zero.
*/
struct dm_block_validator {
const char *name;
void (*prepare_for_write)(const struct dm_block_validator *v,
struct dm_block *b, size_t block_size);
/*
* Return 0 if the checksum is valid or < 0 on error.
*/
int (*check)(const struct dm_block_validator *v,
struct dm_block *b, size_t block_size);
};
/*----------------------------------------------------------------*/
/*
* You can have multiple concurrent readers or a single writer holding a
* block lock.
*/
/*
* dm_bm_lock() locks a block and returns through @result a pointer to
* memory that holds a copy of that block. If you have write-locked the
* block then any changes you make to memory pointed to by @result will be
* written back to the disk sometime after dm_bm_unlock is called.
*/
int dm_bm_read_lock(struct dm_block_manager *bm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result);
int dm_bm_write_lock(struct dm_block_manager *bm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result);
/*
* The *_try_lock variants return -EWOULDBLOCK if the block isn't
* available immediately.
*/
int dm_bm_read_try_lock(struct dm_block_manager *bm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result);
/*
* Use dm_bm_write_lock_zero() when you know you're going to
* overwrite the block completely. It saves a disk read.
*/
int dm_bm_write_lock_zero(struct dm_block_manager *bm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result);
void dm_bm_unlock(struct dm_block *b);
/*
* It's a common idiom to have a superblock that should be committed last.
*
* @superblock should be write-locked on entry. It will be unlocked during
* this function. All dirty blocks are guaranteed to be written and flushed
* before the superblock.
*
* This method always blocks.
*/
int dm_bm_flush(struct dm_block_manager *bm);
/*
* Request data is prefetched into the cache.
*/
void dm_bm_prefetch(struct dm_block_manager *bm, dm_block_t b);
/*
* Switches the bm to a read only mode. Once read-only mode
* has been entered the following functions will return -EPERM.
*
* dm_bm_write_lock
* dm_bm_write_lock_zero
* dm_bm_flush_and_unlock
*
* Additionally you should not use dm_bm_unlock_move, however no error will
* be returned if you do.
*/
bool dm_bm_is_read_only(struct dm_block_manager *bm);
void dm_bm_set_read_only(struct dm_block_manager *bm);
void dm_bm_set_read_write(struct dm_block_manager *bm);
u32 dm_bm_checksum(const void *data, size_t len, u32 init_xor);
/*----------------------------------------------------------------*/
#endif /* _LINUX_DM_BLOCK_MANAGER_H */
@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef DM_BTREE_INTERNAL_H
#define DM_BTREE_INTERNAL_H
#include "dm-btree.h"
/*----------------------------------------------------------------*/
/*
* We'll need 2 accessor functions for n->csum and n->blocknr
* to support dm-btree-spine.c in that case.
*/
enum node_flags {
INTERNAL_NODE = 1,
LEAF_NODE = 1 << 1
};
/*
* Every btree node begins with this structure. Make sure it's a multiple
* of 8-bytes in size, otherwise the 64bit keys will be mis-aligned.
*/
struct node_header {
__le32 csum;
__le32 flags;
__le64 blocknr; /* Block this node is supposed to live in. */
__le32 nr_entries;
__le32 max_entries;
__le32 value_size;
__le32 padding;
} __packed __aligned(8);
struct btree_node {
struct node_header header;
__le64 keys[];
} __packed __aligned(8);
/*
* Locks a block using the btree node validator.
*/
int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result);
void inc_children(struct dm_transaction_manager *tm, struct btree_node *n,
struct dm_btree_value_type *vt);
int new_block(struct dm_btree_info *info, struct dm_block **result);
void unlock_block(struct dm_btree_info *info, struct dm_block *b);
/*
* Spines keep track of the rolling locks. There are 2 variants, read-only
* and one that uses shadowing. These are separate structs to allow the
* type checker to spot misuse, for example accidentally calling read_lock
* on a shadow spine.
*/
struct ro_spine {
struct dm_btree_info *info;
int count;
struct dm_block *nodes[2];
};
void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info);
void exit_ro_spine(struct ro_spine *s);
int ro_step(struct ro_spine *s, dm_block_t new_child);
void ro_pop(struct ro_spine *s);
struct btree_node *ro_node(struct ro_spine *s);
struct shadow_spine {
struct dm_btree_info *info;
int count;
struct dm_block *nodes[2];
dm_block_t root;
};
void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info);
void exit_shadow_spine(struct shadow_spine *s);
int shadow_step(struct shadow_spine *s, dm_block_t b,
struct dm_btree_value_type *vt);
/*
* The spine must have at least one entry before calling this.
*/
struct dm_block *shadow_current(struct shadow_spine *s);
/*
* The spine must have at least two entries before calling this.
*/
struct dm_block *shadow_parent(struct shadow_spine *s);
int shadow_has_parent(struct shadow_spine *s);
dm_block_t shadow_root(struct shadow_spine *s);
/*
* Some inlines.
*/
static inline __le64 *key_ptr(struct btree_node *n, uint32_t index)
{
return n->keys + index;
}
static inline void *value_base(struct btree_node *n)
{
return &n->keys[le32_to_cpu(n->header.max_entries)];
}
static inline void *value_ptr(struct btree_node *n, uint32_t index)
{
uint32_t value_size = le32_to_cpu(n->header.value_size);
return value_base(n) + (value_size * index);
}
/*
* Assumes the values are suitably-aligned and converts to core format.
*/
static inline uint64_t value64(struct btree_node *n, uint32_t index)
{
__le64 *values_le = value_base(n);
return le64_to_cpu(values_le[index]);
}
/*
* Searching for a key within a single node.
*/
int lower_bound(struct btree_node *n, uint64_t key);
extern const struct dm_block_validator btree_node_validator;
/*
* Value type for upper levels of multi-level btrees.
*/
extern void init_le64_type(struct dm_transaction_manager *tm,
struct dm_btree_value_type *vt);
/*
* This returns a shadowed btree leaf that you may modify. In practise
* this means overwrites only, since an insert could cause a node to
* be split. Useful if you need access to the old value to calculate the
* new one.
*
* This only works with single level btrees. The given key must be present in
* the tree, otherwise -EINVAL will be returned.
*/
int btree_get_overwrite_leaf(struct dm_btree_info *info, dm_block_t root,
uint64_t key, int *index,
dm_block_t *new_root, struct dm_block **leaf);
#endif /* DM_BTREE_INTERNAL_H */
@@ -0,0 +1,773 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-btree.h"
#include "dm-btree-internal.h"
#include "dm-transaction-manager.h"
#include <linux/export.h>
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "btree"
/*
* Removing an entry from a btree
* ==============================
*
* A very important constraint for our btree is that no node, except the
* root, may have fewer than a certain number of entries.
* (MIN_ENTRIES <= nr_entries <= MAX_ENTRIES).
*
* Ensuring this is complicated by the way we want to only ever hold the
* locks on 2 nodes concurrently, and only change nodes in a top to bottom
* fashion.
*
* Each node may have a left or right sibling. When decending the spine,
* if a node contains only MIN_ENTRIES then we try and increase this to at
* least MIN_ENTRIES + 1. We do this in the following ways:
*
* [A] No siblings => this can only happen if the node is the root, in which
* case we copy the childs contents over the root.
*
* [B] No left sibling
* ==> rebalance(node, right sibling)
*
* [C] No right sibling
* ==> rebalance(left sibling, node)
*
* [D] Both siblings, total_entries(left, node, right) <= DEL_THRESHOLD
* ==> delete node adding it's contents to left and right
*
* [E] Both siblings, total_entries(left, node, right) > DEL_THRESHOLD
* ==> rebalance(left, node, right)
*
* After these operations it's possible that the our original node no
* longer contains the desired sub tree. For this reason this rebalancing
* is performed on the children of the current node. This also avoids
* having a special case for the root.
*
* Once this rebalancing has occurred we can then step into the child node
* for internal nodes. Or delete the entry for leaf nodes.
*/
/*
* Some little utilities for moving node data around.
*/
static void node_shift(struct btree_node *n, int shift)
{
uint32_t nr_entries = le32_to_cpu(n->header.nr_entries);
uint32_t value_size = le32_to_cpu(n->header.value_size);
if (shift < 0) {
shift = -shift;
BUG_ON(shift > nr_entries);
BUG_ON((void *) key_ptr(n, shift) >= value_ptr(n, shift));
memmove(key_ptr(n, 0),
key_ptr(n, shift),
(nr_entries - shift) * sizeof(__le64));
memmove(value_ptr(n, 0),
value_ptr(n, shift),
(nr_entries - shift) * value_size);
} else {
BUG_ON(nr_entries + shift > le32_to_cpu(n->header.max_entries));
memmove(key_ptr(n, shift),
key_ptr(n, 0),
nr_entries * sizeof(__le64));
memmove(value_ptr(n, shift),
value_ptr(n, 0),
nr_entries * value_size);
}
}
static int node_copy(struct btree_node *left, struct btree_node *right, int shift)
{
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t value_size = le32_to_cpu(left->header.value_size);
if (value_size != le32_to_cpu(right->header.value_size)) {
DMERR("mismatched value size");
return -EILSEQ;
}
if (shift < 0) {
shift = -shift;
if (nr_left + shift > le32_to_cpu(left->header.max_entries)) {
DMERR("bad shift");
return -EINVAL;
}
memcpy(key_ptr(left, nr_left),
key_ptr(right, 0),
shift * sizeof(__le64));
memcpy(value_ptr(left, nr_left),
value_ptr(right, 0),
shift * value_size);
} else {
if (shift > le32_to_cpu(right->header.max_entries)) {
DMERR("bad shift");
return -EINVAL;
}
memcpy(key_ptr(right, 0),
key_ptr(left, nr_left - shift),
shift * sizeof(__le64));
memcpy(value_ptr(right, 0),
value_ptr(left, nr_left - shift),
shift * value_size);
}
return 0;
}
/*
* Delete a specific entry from a leaf node.
*/
static void delete_at(struct btree_node *n, unsigned int index)
{
unsigned int nr_entries = le32_to_cpu(n->header.nr_entries);
unsigned int nr_to_copy = nr_entries - (index + 1);
uint32_t value_size = le32_to_cpu(n->header.value_size);
BUG_ON(index >= nr_entries);
if (nr_to_copy) {
memmove(key_ptr(n, index),
key_ptr(n, index + 1),
nr_to_copy * sizeof(__le64));
memmove(value_ptr(n, index),
value_ptr(n, index + 1),
nr_to_copy * value_size);
}
n->header.nr_entries = cpu_to_le32(nr_entries - 1);
}
static unsigned int merge_threshold(struct btree_node *n)
{
return le32_to_cpu(n->header.max_entries) / 3;
}
struct child {
unsigned int index;
struct dm_block *block;
struct btree_node *n;
};
static int init_child(struct dm_btree_info *info, struct dm_btree_value_type *vt,
struct btree_node *parent,
unsigned int index, struct child *result)
{
int r, inc;
dm_block_t root;
result->index = index;
root = value64(parent, index);
r = dm_tm_shadow_block(info->tm, root, &btree_node_validator,
&result->block, &inc);
if (r)
return r;
result->n = dm_block_data(result->block);
if (inc)
inc_children(info->tm, result->n, vt);
*((__le64 *) value_ptr(parent, index)) =
cpu_to_le64(dm_block_location(result->block));
return 0;
}
static void exit_child(struct dm_btree_info *info, struct child *c)
{
dm_tm_unlock(info->tm, c->block);
}
static int shift(struct btree_node *left, struct btree_node *right, int count)
{
int r;
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
uint32_t max_entries = le32_to_cpu(left->header.max_entries);
uint32_t r_max_entries = le32_to_cpu(right->header.max_entries);
if (max_entries != r_max_entries) {
DMERR("node max_entries mismatch");
return -EILSEQ;
}
if (nr_left - count > max_entries) {
DMERR("node shift out of bounds");
return -EINVAL;
}
if (nr_right + count > max_entries) {
DMERR("node shift out of bounds");
return -EINVAL;
}
if (!count)
return 0;
if (count > 0) {
node_shift(right, count);
r = node_copy(left, right, count);
if (r)
return r;
} else {
r = node_copy(left, right, count);
if (r)
return r;
node_shift(right, count);
}
left->header.nr_entries = cpu_to_le32(nr_left - count);
right->header.nr_entries = cpu_to_le32(nr_right + count);
return 0;
}
static int __rebalance2(struct dm_btree_info *info, struct btree_node *parent,
struct child *l, struct child *r)
{
int ret;
struct btree_node *left = l->n;
struct btree_node *right = r->n;
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
/*
* Ensure the number of entries in each child will be greater
* than or equal to (max_entries / 3 + 1), so no matter which
* child is used for removal, the number will still be not
* less than (max_entries / 3).
*/
unsigned int threshold = 2 * (merge_threshold(left) + 1);
if (nr_left + nr_right < threshold) {
/*
* Merge
*/
node_copy(left, right, -nr_right);
left->header.nr_entries = cpu_to_le32(nr_left + nr_right);
delete_at(parent, r->index);
/*
* We need to decrement the right block, but not it's
* children, since they're still referenced by left.
*/
dm_tm_dec(info->tm, dm_block_location(r->block));
} else {
/*
* Rebalance.
*/
unsigned int target_left = (nr_left + nr_right) / 2;
ret = shift(left, right, nr_left - target_left);
if (ret)
return ret;
*key_ptr(parent, r->index) = right->keys[0];
}
return 0;
}
static int rebalance2(struct shadow_spine *s, struct dm_btree_info *info,
struct dm_btree_value_type *vt, unsigned int left_index)
{
int r;
struct btree_node *parent;
struct child left, right;
parent = dm_block_data(shadow_current(s));
r = init_child(info, vt, parent, left_index, &left);
if (r)
return r;
r = init_child(info, vt, parent, left_index + 1, &right);
if (r) {
exit_child(info, &left);
return r;
}
r = __rebalance2(info, parent, &left, &right);
exit_child(info, &left);
exit_child(info, &right);
return r;
}
/*
* We dump as many entries from center as possible into left, then the rest
* in right, then rebalance2. This wastes some cpu, but I want something
* simple atm.
*/
static int delete_center_node(struct dm_btree_info *info, struct btree_node *parent,
struct child *l, struct child *c, struct child *r,
struct btree_node *left, struct btree_node *center, struct btree_node *right,
uint32_t nr_left, uint32_t nr_center, uint32_t nr_right)
{
uint32_t max_entries = le32_to_cpu(left->header.max_entries);
unsigned int shift = min(max_entries - nr_left, nr_center);
if (nr_left + shift > max_entries) {
DMERR("node shift out of bounds");
return -EINVAL;
}
node_copy(left, center, -shift);
left->header.nr_entries = cpu_to_le32(nr_left + shift);
if (shift != nr_center) {
shift = nr_center - shift;
if ((nr_right + shift) > max_entries) {
DMERR("node shift out of bounds");
return -EINVAL;
}
node_shift(right, shift);
node_copy(center, right, shift);
right->header.nr_entries = cpu_to_le32(nr_right + shift);
}
*key_ptr(parent, r->index) = right->keys[0];
delete_at(parent, c->index);
r->index--;
dm_tm_dec(info->tm, dm_block_location(c->block));
return __rebalance2(info, parent, l, r);
}
/*
* Redistributes entries among 3 sibling nodes.
*/
static int redistribute3(struct dm_btree_info *info, struct btree_node *parent,
struct child *l, struct child *c, struct child *r,
struct btree_node *left, struct btree_node *center, struct btree_node *right,
uint32_t nr_left, uint32_t nr_center, uint32_t nr_right)
{
int s, ret;
uint32_t max_entries = le32_to_cpu(left->header.max_entries);
unsigned int total = nr_left + nr_center + nr_right;
unsigned int target_right = total / 3;
unsigned int remainder = (target_right * 3) != total;
unsigned int target_left = target_right + remainder;
BUG_ON(target_left > max_entries);
BUG_ON(target_right > max_entries);
if (nr_left < nr_right) {
s = nr_left - target_left;
if (s < 0 && nr_center < -s) {
/* not enough in central node */
ret = shift(left, center, -nr_center);
if (ret)
return ret;
s += nr_center;
ret = shift(left, right, s);
if (ret)
return ret;
nr_right += s;
} else {
ret = shift(left, center, s);
if (ret)
return ret;
}
ret = shift(center, right, target_right - nr_right);
if (ret)
return ret;
} else {
s = target_right - nr_right;
if (s > 0 && nr_center < s) {
/* not enough in central node */
ret = shift(center, right, nr_center);
if (ret)
return ret;
s -= nr_center;
ret = shift(left, right, s);
if (ret)
return ret;
nr_left -= s;
} else {
ret = shift(center, right, s);
if (ret)
return ret;
}
ret = shift(left, center, nr_left - target_left);
if (ret)
return ret;
}
*key_ptr(parent, c->index) = center->keys[0];
*key_ptr(parent, r->index) = right->keys[0];
return 0;
}
static int __rebalance3(struct dm_btree_info *info, struct btree_node *parent,
struct child *l, struct child *c, struct child *r)
{
struct btree_node *left = l->n;
struct btree_node *center = c->n;
struct btree_node *right = r->n;
uint32_t nr_left = le32_to_cpu(left->header.nr_entries);
uint32_t nr_center = le32_to_cpu(center->header.nr_entries);
uint32_t nr_right = le32_to_cpu(right->header.nr_entries);
unsigned int threshold = merge_threshold(left) * 4 + 1;
if ((left->header.max_entries != center->header.max_entries) ||
(center->header.max_entries != right->header.max_entries)) {
DMERR("bad btree metadata, max_entries differ");
return -EILSEQ;
}
if ((nr_left + nr_center + nr_right) < threshold) {
return delete_center_node(info, parent, l, c, r, left, center, right,
nr_left, nr_center, nr_right);
}
return redistribute3(info, parent, l, c, r, left, center, right,
nr_left, nr_center, nr_right);
}
static int rebalance3(struct shadow_spine *s, struct dm_btree_info *info,
struct dm_btree_value_type *vt, unsigned int left_index)
{
int r;
struct btree_node *parent = dm_block_data(shadow_current(s));
struct child left, center, right;
/*
* FIXME: fill out an array?
*/
r = init_child(info, vt, parent, left_index, &left);
if (r)
return r;
r = init_child(info, vt, parent, left_index + 1, &center);
if (r) {
exit_child(info, &left);
return r;
}
r = init_child(info, vt, parent, left_index + 2, &right);
if (r) {
exit_child(info, &left);
exit_child(info, &center);
return r;
}
r = __rebalance3(info, parent, &left, &center, &right);
exit_child(info, &left);
exit_child(info, &center);
exit_child(info, &right);
return r;
}
static int rebalance_children(struct shadow_spine *s,
struct dm_btree_info *info,
struct dm_btree_value_type *vt, uint64_t key)
{
int i, r, has_left_sibling, has_right_sibling;
struct btree_node *n;
n = dm_block_data(shadow_current(s));
if (le32_to_cpu(n->header.nr_entries) == 1) {
struct dm_block *child;
int is_shared;
dm_block_t b = value64(n, 0);
r = dm_tm_block_is_shared(info->tm, b, &is_shared);
if (r)
return r;
r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child);
if (r)
return r;
if (is_shared)
inc_children(info->tm, dm_block_data(child), vt);
memcpy(n, dm_block_data(child),
dm_bm_block_size(dm_tm_get_bm(info->tm)));
dm_tm_dec(info->tm, dm_block_location(child));
dm_tm_unlock(info->tm, child);
return 0;
}
i = lower_bound(n, key);
if (i < 0)
return -ENODATA;
has_left_sibling = i > 0;
has_right_sibling = i < (le32_to_cpu(n->header.nr_entries) - 1);
if (!has_left_sibling)
r = rebalance2(s, info, vt, i);
else if (!has_right_sibling)
r = rebalance2(s, info, vt, i - 1);
else
r = rebalance3(s, info, vt, i - 1);
return r;
}
static int do_leaf(struct btree_node *n, uint64_t key, unsigned int *index)
{
int i = lower_bound(n, key);
if ((i < 0) ||
(i >= le32_to_cpu(n->header.nr_entries)) ||
(le64_to_cpu(n->keys[i]) != key))
return -ENODATA;
*index = i;
return 0;
}
/*
* Prepares for removal from one level of the hierarchy. The caller must
* call delete_at() to remove the entry at index.
*/
static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info,
struct dm_btree_value_type *vt, dm_block_t root,
uint64_t key, unsigned int *index)
{
int i = *index, r;
struct btree_node *n;
for (;;) {
r = shadow_step(s, root, vt);
if (r < 0)
break;
/*
* We have to patch up the parent node, ugly, but I don't
* see a way to do this automatically as part of the spine
* op.
*/
if (shadow_has_parent(s)) {
__le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
memcpy(value_ptr(dm_block_data(shadow_parent(s)), i),
&location, sizeof(__le64));
}
n = dm_block_data(shadow_current(s));
if (le32_to_cpu(n->header.flags) & LEAF_NODE)
return do_leaf(n, key, index);
r = rebalance_children(s, info, vt, key);
if (r)
break;
n = dm_block_data(shadow_current(s));
if (le32_to_cpu(n->header.flags) & LEAF_NODE)
return do_leaf(n, key, index);
i = lower_bound(n, key);
/*
* We know the key is present, or else
* rebalance_children would have returned
* -ENODATA
*/
root = value64(n, i);
}
return r;
}
int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, dm_block_t *new_root)
{
unsigned int level, last_level = info->levels - 1;
int index = 0, r = 0;
struct shadow_spine spine;
struct btree_node *n;
struct dm_btree_value_type le64_vt;
init_le64_type(info->tm, &le64_vt);
init_shadow_spine(&spine, info);
for (level = 0; level < info->levels; level++) {
r = remove_raw(&spine, info,
(level == last_level ?
&info->value_type : &le64_vt),
root, keys[level], (unsigned int *)&index);
if (r < 0)
break;
n = dm_block_data(shadow_current(&spine));
if (level != last_level) {
root = value64(n, index);
continue;
}
BUG_ON(index < 0 || index >= le32_to_cpu(n->header.nr_entries));
if (info->value_type.dec)
info->value_type.dec(info->value_type.context,
value_ptr(n, index), 1);
delete_at(n, index);
}
if (!r)
*new_root = shadow_root(&spine);
exit_shadow_spine(&spine);
return r;
}
EXPORT_SYMBOL_GPL(dm_btree_remove);
/*----------------------------------------------------------------*/
static int remove_nearest(struct shadow_spine *s, struct dm_btree_info *info,
struct dm_btree_value_type *vt, dm_block_t root,
uint64_t key, int *index)
{
int i = *index, r;
struct btree_node *n;
for (;;) {
r = shadow_step(s, root, vt);
if (r < 0)
break;
/*
* We have to patch up the parent node, ugly, but I don't
* see a way to do this automatically as part of the spine
* op.
*/
if (shadow_has_parent(s)) {
__le64 location = cpu_to_le64(dm_block_location(shadow_current(s)));
memcpy(value_ptr(dm_block_data(shadow_parent(s)), i),
&location, sizeof(__le64));
}
n = dm_block_data(shadow_current(s));
if (le32_to_cpu(n->header.flags) & LEAF_NODE) {
*index = lower_bound(n, key);
return 0;
}
r = rebalance_children(s, info, vt, key);
if (r)
break;
n = dm_block_data(shadow_current(s));
if (le32_to_cpu(n->header.flags) & LEAF_NODE) {
*index = lower_bound(n, key);
return 0;
}
i = lower_bound(n, key);
/*
* We know the key is present, or else
* rebalance_children would have returned
* -ENODATA
*/
root = value64(n, i);
}
return r;
}
static int remove_one(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, uint64_t end_key,
dm_block_t *new_root, unsigned int *nr_removed)
{
unsigned int level, last_level = info->levels - 1;
int index = 0, r = 0;
struct shadow_spine spine;
struct btree_node *n;
struct dm_btree_value_type le64_vt;
uint64_t k;
init_le64_type(info->tm, &le64_vt);
init_shadow_spine(&spine, info);
for (level = 0; level < last_level; level++) {
r = remove_raw(&spine, info, &le64_vt,
root, keys[level], (unsigned int *) &index);
if (r < 0)
goto out;
n = dm_block_data(shadow_current(&spine));
root = value64(n, index);
}
r = remove_nearest(&spine, info, &info->value_type,
root, keys[last_level], &index);
if (r < 0)
goto out;
n = dm_block_data(shadow_current(&spine));
if (index < 0)
index = 0;
if (index >= le32_to_cpu(n->header.nr_entries)) {
r = -ENODATA;
goto out;
}
k = le64_to_cpu(n->keys[index]);
if (k >= keys[last_level] && k < end_key) {
if (info->value_type.dec)
info->value_type.dec(info->value_type.context,
value_ptr(n, index), 1);
delete_at(n, index);
keys[last_level] = k + 1ull;
} else
r = -ENODATA;
out:
*new_root = shadow_root(&spine);
exit_shadow_spine(&spine);
return r;
}
int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root,
uint64_t *first_key, uint64_t end_key,
dm_block_t *new_root, unsigned int *nr_removed)
{
int r;
*nr_removed = 0;
do {
r = remove_one(info, root, first_key, end_key, &root, nr_removed);
if (!r)
(*nr_removed)++;
} while (!r);
*new_root = root;
return r == -ENODATA ? 0 : r;
}
EXPORT_SYMBOL_GPL(dm_btree_remove_leaves);
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-btree-internal.h"
#include "dm-transaction-manager.h"
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "btree spine"
/*----------------------------------------------------------------*/
#define BTREE_CSUM_XOR 121107
static void node_prepare_for_write(const struct dm_block_validator *v,
struct dm_block *b,
size_t block_size)
{
struct btree_node *n = dm_block_data(b);
struct node_header *h = &n->header;
h->blocknr = cpu_to_le64(dm_block_location(b));
h->csum = cpu_to_le32(dm_bm_checksum(&h->flags,
block_size - sizeof(__le32),
BTREE_CSUM_XOR));
}
static int node_check(const struct dm_block_validator *v,
struct dm_block *b,
size_t block_size)
{
struct btree_node *n = dm_block_data(b);
struct node_header *h = &n->header;
size_t value_size;
__le32 csum_disk;
uint32_t flags, nr_entries, max_entries;
if (dm_block_location(b) != le64_to_cpu(h->blocknr)) {
DMERR_LIMIT("%s failed: blocknr %llu != wanted %llu", __func__,
le64_to_cpu(h->blocknr), dm_block_location(b));
return -ENOTBLK;
}
csum_disk = cpu_to_le32(dm_bm_checksum(&h->flags,
block_size - sizeof(__le32),
BTREE_CSUM_XOR));
if (csum_disk != h->csum) {
DMERR_LIMIT("%s failed: csum %u != wanted %u", __func__,
le32_to_cpu(csum_disk), le32_to_cpu(h->csum));
return -EILSEQ;
}
nr_entries = le32_to_cpu(h->nr_entries);
max_entries = le32_to_cpu(h->max_entries);
value_size = le32_to_cpu(h->value_size);
if (sizeof(struct node_header) +
(sizeof(__le64) + value_size) * max_entries > block_size) {
DMERR_LIMIT("%s failed: max_entries too large", __func__);
return -EILSEQ;
}
if (nr_entries > max_entries) {
DMERR_LIMIT("%s failed: too many entries", __func__);
return -EILSEQ;
}
/*
* The node must be either INTERNAL or LEAF.
*/
flags = le32_to_cpu(h->flags);
if (!(flags & INTERNAL_NODE) && !(flags & LEAF_NODE)) {
DMERR_LIMIT("%s failed: node is neither INTERNAL or LEAF", __func__);
return -EILSEQ;
}
return 0;
}
const struct dm_block_validator btree_node_validator = {
.name = "btree_node",
.prepare_for_write = node_prepare_for_write,
.check = node_check
};
/*----------------------------------------------------------------*/
int bn_read_lock(struct dm_btree_info *info, dm_block_t b,
struct dm_block **result)
{
return dm_tm_read_lock(info->tm, b, &btree_node_validator, result);
}
static int bn_shadow(struct dm_btree_info *info, dm_block_t orig,
struct dm_btree_value_type *vt,
struct dm_block **result)
{
int r, inc;
r = dm_tm_shadow_block(info->tm, orig, &btree_node_validator,
result, &inc);
if (!r && inc)
inc_children(info->tm, dm_block_data(*result), vt);
return r;
}
int new_block(struct dm_btree_info *info, struct dm_block **result)
{
return dm_tm_new_block(info->tm, &btree_node_validator, result);
}
void unlock_block(struct dm_btree_info *info, struct dm_block *b)
{
dm_tm_unlock(info->tm, b);
}
/*----------------------------------------------------------------*/
void init_ro_spine(struct ro_spine *s, struct dm_btree_info *info)
{
s->info = info;
s->count = 0;
s->nodes[0] = NULL;
s->nodes[1] = NULL;
}
void exit_ro_spine(struct ro_spine *s)
{
int i;
for (i = 0; i < s->count; i++)
unlock_block(s->info, s->nodes[i]);
}
int ro_step(struct ro_spine *s, dm_block_t new_child)
{
int r;
if (s->count == 2) {
unlock_block(s->info, s->nodes[0]);
s->nodes[0] = s->nodes[1];
s->count--;
}
r = bn_read_lock(s->info, new_child, s->nodes + s->count);
if (!r)
s->count++;
return r;
}
void ro_pop(struct ro_spine *s)
{
BUG_ON(!s->count);
--s->count;
unlock_block(s->info, s->nodes[s->count]);
}
struct btree_node *ro_node(struct ro_spine *s)
{
struct dm_block *block;
BUG_ON(!s->count);
block = s->nodes[s->count - 1];
return dm_block_data(block);
}
/*----------------------------------------------------------------*/
void init_shadow_spine(struct shadow_spine *s, struct dm_btree_info *info)
{
s->info = info;
s->count = 0;
}
void exit_shadow_spine(struct shadow_spine *s)
{
int i;
for (i = 0; i < s->count; i++)
unlock_block(s->info, s->nodes[i]);
}
int shadow_step(struct shadow_spine *s, dm_block_t b,
struct dm_btree_value_type *vt)
{
int r;
if (s->count == 2) {
unlock_block(s->info, s->nodes[0]);
s->nodes[0] = s->nodes[1];
s->count--;
}
r = bn_shadow(s->info, b, vt, s->nodes + s->count);
if (!r) {
if (!s->count)
s->root = dm_block_location(s->nodes[0]);
s->count++;
}
return r;
}
struct dm_block *shadow_current(struct shadow_spine *s)
{
BUG_ON(!s->count);
return s->nodes[s->count - 1];
}
struct dm_block *shadow_parent(struct shadow_spine *s)
{
BUG_ON(s->count != 2);
return s->count == 2 ? s->nodes[0] : NULL;
}
int shadow_has_parent(struct shadow_spine *s)
{
return s->count >= 2;
}
dm_block_t shadow_root(struct shadow_spine *s)
{
return s->root;
}
static void le64_inc(void *context, const void *value_le, unsigned int count)
{
dm_tm_with_runs(context, value_le, count, dm_tm_inc_range);
}
static void le64_dec(void *context, const void *value_le, unsigned int count)
{
dm_tm_with_runs(context, value_le, count, dm_tm_dec_range);
}
static int le64_equal(void *context, const void *value1_le, const void *value2_le)
{
__le64 v1_le, v2_le;
memcpy(&v1_le, value1_le, sizeof(v1_le));
memcpy(&v2_le, value2_le, sizeof(v2_le));
return v1_le == v2_le;
}
void init_le64_type(struct dm_transaction_manager *tm,
struct dm_btree_value_type *vt)
{
vt->context = tm;
vt->size = sizeof(__le64);
vt->inc = le64_inc;
vt->dec = le64_dec;
vt->equal = le64_equal;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,216 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_BTREE_H
#define _LINUX_DM_BTREE_H
#include "dm-block-manager.h"
struct dm_transaction_manager;
/*----------------------------------------------------------------*/
/*
* Annotations used to check on-disk metadata is handled as little-endian.
*/
#ifdef __CHECKER__
# define __dm_written_to_disk(x) __releases(x)
# define __dm_reads_from_disk(x) __acquires(x)
# define __dm_bless_for_disk(x) __acquire(x)
# define __dm_unbless_for_disk(x) __release(x)
#else
# define __dm_written_to_disk(x)
# define __dm_reads_from_disk(x)
# define __dm_bless_for_disk(x)
# define __dm_unbless_for_disk(x)
#endif
/*----------------------------------------------------------------*/
/*
* Manipulates hierarchical B+ trees with 64-bit keys and arbitrary-sized
* values.
*/
/*
* Information about the values stored within the btree.
*/
struct dm_btree_value_type {
void *context;
/*
* The size in bytes of each value.
*/
uint32_t size;
/*
* Any of these methods can be safely set to NULL if you do not
* need the corresponding feature.
*/
/*
* The btree is making a duplicate of a run of values, for instance
* because previously-shared btree nodes have now diverged.
* @value argument is the new copy that the copy function may modify.
* (Probably it just wants to increment a reference count
* somewhere.) This method is _not_ called for insertion of a new
* value: It is assumed the ref count is already 1.
*/
void (*inc)(void *context, const void *value, unsigned int count);
/*
* These values are being deleted. The btree takes care of freeing
* the memory pointed to by @value. Often the del function just
* needs to decrement a reference counts somewhere.
*/
void (*dec)(void *context, const void *value, unsigned int count);
/*
* A test for equality between two values. When a value is
* overwritten with a new one, the old one has the dec method
* called _unless_ the new and old value are deemed equal.
*/
int (*equal)(void *context, const void *value1, const void *value2);
};
/*
* The shape and contents of a btree.
*/
struct dm_btree_info {
struct dm_transaction_manager *tm;
/*
* Number of nested btrees. (Not the depth of a single tree.)
*/
unsigned int levels;
struct dm_btree_value_type value_type;
};
/*
* Set up an empty tree. O(1).
*/
int dm_btree_empty(struct dm_btree_info *info, dm_block_t *root);
/*
* Delete a tree. O(n) - this is the slow one! It can also block, so
* please don't call it on an IO path.
*/
int dm_btree_del(struct dm_btree_info *info, dm_block_t root);
/*
* All the lookup functions return -ENODATA if the key cannot be found.
*/
/*
* Tries to find a key that matches exactly. O(ln(n))
*/
int dm_btree_lookup(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, void *value_le);
/*
* Tries to find the first key where the bottom level key is >= to that
* given. Useful for skipping empty sections of the btree.
*/
int dm_btree_lookup_next(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, uint64_t *rkey, void *value_le);
/*
* Insertion (or overwrite an existing value). O(ln(n))
*/
int dm_btree_insert(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, void *value, dm_block_t *new_root)
__dm_written_to_disk(value);
/*
* A variant of insert that indicates whether it actually inserted or just
* overwrote. Useful if you're keeping track of the number of entries in a
* tree.
*/
int dm_btree_insert_notify(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, void *value, dm_block_t *new_root,
int *inserted)
__dm_written_to_disk(value);
/*
* Remove a key if present. This doesn't remove empty sub trees. Normally
* subtrees represent a separate entity, like a snapshot map, so this is
* correct behaviour. O(ln(n)).
*/
int dm_btree_remove(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, dm_block_t *new_root);
/*
* Removes a _contiguous_ run of values starting from 'keys' and not
* reaching keys2 (where keys2 is keys with the final key replaced with
* 'end_key'). 'end_key' is the one-past-the-end value. 'keys' may be
* altered.
*/
int dm_btree_remove_leaves(struct dm_btree_info *info, dm_block_t root,
uint64_t *keys, uint64_t end_key,
dm_block_t *new_root, unsigned int *nr_removed);
/*
* Returns < 0 on failure. Otherwise the number of key entries that have
* been filled out. Remember trees can have zero entries, and as such have
* no lowest key.
*/
int dm_btree_find_lowest_key(struct dm_btree_info *info, dm_block_t root,
uint64_t *result_keys);
/*
* Returns < 0 on failure. Otherwise the number of key entries that have
* been filled out. Remember trees can have zero entries, and as such have
* no highest key.
*/
int dm_btree_find_highest_key(struct dm_btree_info *info, dm_block_t root,
uint64_t *result_keys);
/*
* Iterate through the a btree, calling fn() on each entry.
* It only works for single level trees and is internally recursive, so
* monitor stack usage carefully.
*/
int dm_btree_walk(struct dm_btree_info *info, dm_block_t root,
int (*fn)(void *context, uint64_t *keys, void *leaf),
void *context);
/*----------------------------------------------------------------*/
/*
* Cursor API. This does not follow the rolling lock convention. Since we
* know the order that values are required we can issue prefetches to speed
* up iteration. Use on a single level btree only.
*/
#define DM_BTREE_CURSOR_MAX_DEPTH 16
struct cursor_node {
struct dm_block *b;
unsigned int index;
};
struct dm_btree_cursor {
struct dm_btree_info *info;
dm_block_t root;
bool prefetch_leaves;
unsigned int depth;
struct cursor_node nodes[DM_BTREE_CURSOR_MAX_DEPTH];
};
/*
* Creates a fresh cursor. If prefetch_leaves is set then it is assumed
* the btree contains block indexes that will be prefetched. The cursor is
* quite large, so you probably don't want to put it on the stack.
*/
int dm_btree_cursor_begin(struct dm_btree_info *info, dm_block_t root,
bool prefetch_leaves, struct dm_btree_cursor *c);
void dm_btree_cursor_end(struct dm_btree_cursor *c);
int dm_btree_cursor_next(struct dm_btree_cursor *c);
int dm_btree_cursor_skip(struct dm_btree_cursor *c, uint32_t count);
int dm_btree_cursor_get_value(struct dm_btree_cursor *c, uint64_t *key, void *value_le);
#endif /* _LINUX_DM_BTREE_H */
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _DM_PERSISTENT_DATA_INTERNAL_H
#define _DM_PERSISTENT_DATA_INTERNAL_H
#include "dm-block-manager.h"
static inline unsigned int dm_hash_block(dm_block_t b, unsigned int hash_mask)
{
const unsigned int BIG_PRIME = 4294967291UL;
return (((unsigned int) b) * BIG_PRIME) & hash_mask;
}
#endif /* _PERSISTENT_DATA_INTERNAL_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,146 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef DM_SPACE_MAP_COMMON_H
#define DM_SPACE_MAP_COMMON_H
#include "dm-btree.h"
/*----------------------------------------------------------------*/
/*
* Low level disk format
*
* Bitmap btree
* ------------
*
* Each value stored in the btree is an index_entry. This points to a
* block that is used as a bitmap. Within the bitmap hold 2 bits per
* entry, which represent UNUSED = 0, REF_COUNT = 1, REF_COUNT = 2 and
* REF_COUNT = many.
*
* Refcount btree
* --------------
*
* Any entry that has a ref count higher than 2 gets entered in the ref
* count tree. The leaf values for this tree is the 32-bit ref count.
*/
struct disk_index_entry {
__le64 blocknr;
__le32 nr_free;
__le32 none_free_before;
} __packed __aligned(8);
#define MAX_METADATA_BITMAPS 255
struct disk_metadata_index {
__le32 csum;
__le32 padding;
__le64 blocknr;
struct disk_index_entry index[MAX_METADATA_BITMAPS];
} __packed __aligned(8);
struct ll_disk;
typedef int (*load_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *result);
typedef int (*save_ie_fn)(struct ll_disk *ll, dm_block_t index, struct disk_index_entry *ie);
typedef int (*init_index_fn)(struct ll_disk *ll);
typedef int (*open_index_fn)(struct ll_disk *ll);
typedef dm_block_t (*max_index_entries_fn)(struct ll_disk *ll);
typedef int (*commit_fn)(struct ll_disk *ll);
/*
* A lot of time can be wasted reading and writing the same
* index entry. So we cache a few entries.
*/
#define IE_CACHE_SIZE 64
#define IE_CACHE_MASK (IE_CACHE_SIZE - 1)
struct ie_cache {
bool valid;
bool dirty;
dm_block_t index;
struct disk_index_entry ie;
};
struct ll_disk {
struct dm_transaction_manager *tm;
struct dm_btree_info bitmap_info;
struct dm_btree_info ref_count_info;
uint32_t block_size;
uint32_t entries_per_block;
dm_block_t nr_blocks;
dm_block_t nr_allocated;
/*
* bitmap_root may be a btree root or a simple index.
*/
dm_block_t bitmap_root;
dm_block_t ref_count_root;
struct disk_metadata_index mi_le;
load_ie_fn load_ie;
save_ie_fn save_ie;
init_index_fn init_index;
open_index_fn open_index;
max_index_entries_fn max_entries;
commit_fn commit;
bool bitmap_index_changed:1;
struct ie_cache ie_cache[IE_CACHE_SIZE];
};
struct disk_sm_root {
__le64 nr_blocks;
__le64 nr_allocated;
__le64 bitmap_root;
__le64 ref_count_root;
} __packed __aligned(8);
#define ENTRIES_PER_BYTE 4
struct disk_bitmap_header {
__le32 csum;
__le32 not_used;
__le64 blocknr;
} __packed __aligned(8);
/*----------------------------------------------------------------*/
int sm_ll_extend(struct ll_disk *ll, dm_block_t extra_blocks);
int sm_ll_lookup_bitmap(struct ll_disk *ll, dm_block_t b, uint32_t *result);
int sm_ll_lookup(struct ll_disk *ll, dm_block_t b, uint32_t *result);
int sm_ll_find_free_block(struct ll_disk *ll, dm_block_t begin,
dm_block_t end, dm_block_t *result);
int sm_ll_find_common_free_block(struct ll_disk *old_ll, struct ll_disk *new_ll,
dm_block_t begin, dm_block_t end, dm_block_t *result);
/*
* The next three functions return (via nr_allocations) the net number of
* allocations that were made. This number may be negative if there were
* more frees than allocs.
*/
int sm_ll_insert(struct ll_disk *ll, dm_block_t b, uint32_t ref_count, int32_t *nr_allocations);
int sm_ll_inc(struct ll_disk *ll, dm_block_t b, dm_block_t e, int32_t *nr_allocations);
int sm_ll_dec(struct ll_disk *ll, dm_block_t b, dm_block_t e, int32_t *nr_allocations);
int sm_ll_commit(struct ll_disk *ll);
int sm_ll_new_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm);
int sm_ll_open_metadata(struct ll_disk *ll, struct dm_transaction_manager *tm,
void *root_le, size_t len);
int sm_ll_new_disk(struct ll_disk *ll, struct dm_transaction_manager *tm);
int sm_ll_open_disk(struct ll_disk *ll, struct dm_transaction_manager *tm,
void *root_le, size_t len);
/*----------------------------------------------------------------*/
#endif /* DM_SPACE_MAP_COMMON_H */
@@ -0,0 +1,281 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-space-map-common.h"
#include "dm-space-map-disk.h"
#include "dm-space-map.h"
#include "dm-transaction-manager.h"
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "space map disk"
/*----------------------------------------------------------------*/
/*
* Space map interface.
*/
struct sm_disk {
struct dm_space_map sm;
struct ll_disk ll;
struct ll_disk old_ll;
dm_block_t begin;
dm_block_t nr_allocated_this_transaction;
};
static void sm_disk_destroy(struct dm_space_map *sm)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
kfree(smd);
}
static int sm_disk_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
return sm_ll_extend(&smd->ll, extra_blocks);
}
static int sm_disk_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
*count = smd->old_ll.nr_blocks;
return 0;
}
static int sm_disk_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
*count = (smd->old_ll.nr_blocks - smd->old_ll.nr_allocated) - smd->nr_allocated_this_transaction;
return 0;
}
static int sm_disk_get_count(struct dm_space_map *sm, dm_block_t b,
uint32_t *result)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
return sm_ll_lookup(&smd->ll, b, result);
}
static int sm_disk_count_is_more_than_one(struct dm_space_map *sm, dm_block_t b,
int *result)
{
int r;
uint32_t count;
r = sm_disk_get_count(sm, b, &count);
if (r)
return r;
*result = count > 1;
return 0;
}
static int sm_disk_set_count(struct dm_space_map *sm, dm_block_t b,
uint32_t count)
{
int r;
int32_t nr_allocations;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
r = sm_ll_insert(&smd->ll, b, count, &nr_allocations);
if (!r)
smd->nr_allocated_this_transaction += nr_allocations;
return r;
}
static int sm_disk_inc_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r;
int32_t nr_allocations;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
r = sm_ll_inc(&smd->ll, b, e, &nr_allocations);
if (!r)
smd->nr_allocated_this_transaction += nr_allocations;
return r;
}
static int sm_disk_dec_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r;
int32_t nr_allocations;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
r = sm_ll_dec(&smd->ll, b, e, &nr_allocations);
if (!r)
smd->nr_allocated_this_transaction += nr_allocations;
return r;
}
static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b)
{
int r;
int32_t nr_allocations;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
/*
* Any block we allocate has to be free in both the old and current ll.
*/
r = sm_ll_find_common_free_block(&smd->old_ll, &smd->ll, smd->begin, smd->ll.nr_blocks, b);
if (r == -ENOSPC)
/*
* There's no free block between smd->begin and the end of the metadata device.
* We search before smd->begin in case something has been freed.
*/
r = sm_ll_find_common_free_block(&smd->old_ll, &smd->ll, 0, smd->begin, b);
if (r)
return r;
smd->begin = *b + 1;
r = sm_ll_inc(&smd->ll, *b, *b + 1, &nr_allocations);
if (!r)
smd->nr_allocated_this_transaction += nr_allocations;
return r;
}
static int sm_disk_commit(struct dm_space_map *sm)
{
int r;
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
r = sm_ll_commit(&smd->ll);
if (r)
return r;
memcpy(&smd->old_ll, &smd->ll, sizeof(smd->old_ll));
smd->nr_allocated_this_transaction = 0;
return 0;
}
static int sm_disk_root_size(struct dm_space_map *sm, size_t *result)
{
*result = sizeof(struct disk_sm_root);
return 0;
}
static int sm_disk_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
{
struct sm_disk *smd = container_of(sm, struct sm_disk, sm);
struct disk_sm_root root_le;
root_le.nr_blocks = cpu_to_le64(smd->ll.nr_blocks);
root_le.nr_allocated = cpu_to_le64(smd->ll.nr_allocated);
root_le.bitmap_root = cpu_to_le64(smd->ll.bitmap_root);
root_le.ref_count_root = cpu_to_le64(smd->ll.ref_count_root);
if (max < sizeof(root_le))
return -ENOSPC;
memcpy(where_le, &root_le, sizeof(root_le));
return 0;
}
/*----------------------------------------------------------------*/
static struct dm_space_map ops = {
.destroy = sm_disk_destroy,
.extend = sm_disk_extend,
.get_nr_blocks = sm_disk_get_nr_blocks,
.get_nr_free = sm_disk_get_nr_free,
.get_count = sm_disk_get_count,
.count_is_more_than_one = sm_disk_count_is_more_than_one,
.set_count = sm_disk_set_count,
.inc_blocks = sm_disk_inc_blocks,
.dec_blocks = sm_disk_dec_blocks,
.new_block = sm_disk_new_block,
.commit = sm_disk_commit,
.root_size = sm_disk_root_size,
.copy_root = sm_disk_copy_root,
.register_threshold_callback = NULL
};
struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
dm_block_t nr_blocks)
{
int r;
struct sm_disk *smd;
smd = kmalloc_obj(*smd);
if (!smd)
return ERR_PTR(-ENOMEM);
smd->begin = 0;
smd->nr_allocated_this_transaction = 0;
memcpy(&smd->sm, &ops, sizeof(smd->sm));
r = sm_ll_new_disk(&smd->ll, tm);
if (r)
goto bad;
r = sm_ll_extend(&smd->ll, nr_blocks);
if (r)
goto bad;
r = sm_disk_commit(&smd->sm);
if (r)
goto bad;
return &smd->sm;
bad:
kfree(smd);
return ERR_PTR(r);
}
EXPORT_SYMBOL_GPL(dm_sm_disk_create);
struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
void *root_le, size_t len)
{
int r;
struct sm_disk *smd;
smd = kmalloc_obj(*smd);
if (!smd)
return ERR_PTR(-ENOMEM);
smd->begin = 0;
smd->nr_allocated_this_transaction = 0;
memcpy(&smd->sm, &ops, sizeof(smd->sm));
r = sm_ll_open_disk(&smd->ll, tm, root_le, len);
if (r)
goto bad;
r = sm_disk_commit(&smd->sm);
if (r)
goto bad;
return &smd->sm;
bad:
kfree(smd);
return ERR_PTR(r);
}
EXPORT_SYMBOL_GPL(dm_sm_disk_open);
/*----------------------------------------------------------------*/
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_SPACE_MAP_DISK_H
#define _LINUX_DM_SPACE_MAP_DISK_H
#include "dm-block-manager.h"
struct dm_space_map;
struct dm_transaction_manager;
/*
* Unfortunately we have to use two-phase construction due to the cycle
* between the tm and sm.
*/
struct dm_space_map *dm_sm_disk_create(struct dm_transaction_manager *tm,
dm_block_t nr_blocks);
struct dm_space_map *dm_sm_disk_open(struct dm_transaction_manager *tm,
void *root, size_t len);
#endif /* _LINUX_DM_SPACE_MAP_DISK_H */
@@ -0,0 +1,846 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-space-map.h"
#include "dm-space-map-common.h"
#include "dm-space-map-metadata.h"
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/device-mapper.h>
#include <linux/kernel.h>
#define DM_MSG_PREFIX "space map metadata"
/*----------------------------------------------------------------*/
/*
* An edge triggered threshold.
*/
struct threshold {
bool threshold_set;
bool value_set;
dm_block_t threshold;
dm_block_t current_value;
dm_sm_threshold_fn fn;
void *context;
};
static void threshold_init(struct threshold *t)
{
t->threshold_set = false;
t->value_set = false;
}
static void set_threshold(struct threshold *t, dm_block_t value,
dm_sm_threshold_fn fn, void *context)
{
t->threshold_set = true;
t->threshold = value;
t->fn = fn;
t->context = context;
}
static bool below_threshold(struct threshold *t, dm_block_t value)
{
return t->threshold_set && value <= t->threshold;
}
static bool threshold_already_triggered(struct threshold *t)
{
return t->value_set && below_threshold(t, t->current_value);
}
static void check_threshold(struct threshold *t, dm_block_t value)
{
if (below_threshold(t, value) &&
!threshold_already_triggered(t))
t->fn(t->context);
t->value_set = true;
t->current_value = value;
}
/*----------------------------------------------------------------*/
/*
* Space map interface.
*
* The low level disk format is written using the standard btree and
* transaction manager. This means that performing disk operations may
* cause us to recurse into the space map in order to allocate new blocks.
* For this reason we have a pool of pre-allocated blocks large enough to
* service any metadata_ll_disk operation.
*/
/*
* FIXME: we should calculate this based on the size of the device.
* Only the metadata space map needs this functionality.
*/
#define MAX_RECURSIVE_ALLOCATIONS 1024
enum block_op_type {
BOP_INC,
BOP_DEC
};
struct block_op {
enum block_op_type type;
dm_block_t b;
dm_block_t e;
};
struct bop_ring_buffer {
unsigned int begin;
unsigned int end;
struct block_op bops[MAX_RECURSIVE_ALLOCATIONS + 1];
};
static void brb_init(struct bop_ring_buffer *brb)
{
brb->begin = 0;
brb->end = 0;
}
static bool brb_empty(struct bop_ring_buffer *brb)
{
return brb->begin == brb->end;
}
static unsigned int brb_next(struct bop_ring_buffer *brb, unsigned int old)
{
unsigned int r = old + 1;
return r >= ARRAY_SIZE(brb->bops) ? 0 : r;
}
static int brb_push(struct bop_ring_buffer *brb,
enum block_op_type type, dm_block_t b, dm_block_t e)
{
struct block_op *bop;
unsigned int next = brb_next(brb, brb->end);
/*
* We don't allow the last bop to be filled, this way we can
* differentiate between full and empty.
*/
if (next == brb->begin)
return -ENOMEM;
bop = brb->bops + brb->end;
bop->type = type;
bop->b = b;
bop->e = e;
brb->end = next;
return 0;
}
static int brb_peek(struct bop_ring_buffer *brb, struct block_op *result)
{
struct block_op *bop;
if (brb_empty(brb))
return -ENODATA;
bop = brb->bops + brb->begin;
memcpy(result, bop, sizeof(*result));
return 0;
}
static int brb_pop(struct bop_ring_buffer *brb)
{
if (brb_empty(brb))
return -ENODATA;
brb->begin = brb_next(brb, brb->begin);
return 0;
}
/*----------------------------------------------------------------*/
struct sm_metadata {
struct dm_space_map sm;
struct ll_disk ll;
struct ll_disk old_ll;
dm_block_t begin;
unsigned int recursion_count;
unsigned int allocated_this_transaction;
struct bop_ring_buffer uncommitted;
struct threshold threshold;
};
static int add_bop(struct sm_metadata *smm, enum block_op_type type, dm_block_t b, dm_block_t e)
{
int r = brb_push(&smm->uncommitted, type, b, e);
if (r) {
DMERR("too many recursive allocations");
return -ENOMEM;
}
return 0;
}
static int commit_bop(struct sm_metadata *smm, struct block_op *op)
{
int r = 0;
int32_t nr_allocations;
switch (op->type) {
case BOP_INC:
r = sm_ll_inc(&smm->ll, op->b, op->e, &nr_allocations);
break;
case BOP_DEC:
r = sm_ll_dec(&smm->ll, op->b, op->e, &nr_allocations);
break;
}
return r;
}
static void in(struct sm_metadata *smm)
{
smm->recursion_count++;
}
static int apply_bops(struct sm_metadata *smm)
{
int r = 0;
while (!brb_empty(&smm->uncommitted)) {
struct block_op bop;
r = brb_peek(&smm->uncommitted, &bop);
if (r) {
DMERR("bug in bop ring buffer");
break;
}
r = commit_bop(smm, &bop);
if (r)
break;
brb_pop(&smm->uncommitted);
}
return r;
}
static int out(struct sm_metadata *smm)
{
int r = 0;
/*
* If we're not recursing then very bad things are happening.
*/
if (!smm->recursion_count) {
DMERR("lost track of recursion depth");
return -ENOMEM;
}
if (smm->recursion_count == 1)
r = apply_bops(smm);
smm->recursion_count--;
return r;
}
/*
* When using the out() function above, we often want to combine an error
* code for the operation run in the recursive context with that from
* out().
*/
static int combine_errors(int r1, int r2)
{
return r1 ? r1 : r2;
}
static int recursing(struct sm_metadata *smm)
{
return smm->recursion_count;
}
static void sm_metadata_destroy(struct dm_space_map *sm)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
kvfree(smm);
}
static int sm_metadata_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
*count = smm->ll.nr_blocks;
return 0;
}
static int sm_metadata_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
*count = smm->old_ll.nr_blocks - smm->old_ll.nr_allocated -
smm->allocated_this_transaction;
return 0;
}
static int sm_metadata_get_count(struct dm_space_map *sm, dm_block_t b,
uint32_t *result)
{
int r;
unsigned int i;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
unsigned int adjustment = 0;
/*
* We may have some uncommitted adjustments to add. This list
* should always be really short.
*/
for (i = smm->uncommitted.begin;
i != smm->uncommitted.end;
i = brb_next(&smm->uncommitted, i)) {
struct block_op *op = smm->uncommitted.bops + i;
if (b < op->b || b >= op->e)
continue;
switch (op->type) {
case BOP_INC:
adjustment++;
break;
case BOP_DEC:
adjustment--;
break;
}
}
r = sm_ll_lookup(&smm->ll, b, result);
if (r)
return r;
*result += adjustment;
return 0;
}
static int sm_metadata_count_is_more_than_one(struct dm_space_map *sm,
dm_block_t b, int *result)
{
int r, adjustment = 0;
unsigned int i;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
uint32_t rc;
/*
* We may have some uncommitted adjustments to add. This list
* should always be really short.
*/
for (i = smm->uncommitted.begin;
i != smm->uncommitted.end;
i = brb_next(&smm->uncommitted, i)) {
struct block_op *op = smm->uncommitted.bops + i;
if (b < op->b || b >= op->e)
continue;
switch (op->type) {
case BOP_INC:
adjustment++;
break;
case BOP_DEC:
adjustment--;
break;
}
}
if (adjustment > 1) {
*result = 1;
return 0;
}
r = sm_ll_lookup_bitmap(&smm->ll, b, &rc);
if (r)
return r;
if (rc == 3)
/*
* We err on the side of caution, and always return true.
*/
*result = 1;
else
*result = rc + adjustment > 1;
return 0;
}
static int sm_metadata_set_count(struct dm_space_map *sm, dm_block_t b,
uint32_t count)
{
int r, r2;
int32_t nr_allocations;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
if (smm->recursion_count) {
DMERR("cannot recurse set_count()");
return -EINVAL;
}
in(smm);
r = sm_ll_insert(&smm->ll, b, count, &nr_allocations);
r2 = out(smm);
return combine_errors(r, r2);
}
static int sm_metadata_inc_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r, r2 = 0;
int32_t nr_allocations;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
if (recursing(smm)) {
r = add_bop(smm, BOP_INC, b, e);
if (r)
return r;
} else {
in(smm);
r = sm_ll_inc(&smm->ll, b, e, &nr_allocations);
r2 = out(smm);
}
return combine_errors(r, r2);
}
static int sm_metadata_dec_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r, r2 = 0;
int32_t nr_allocations;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
if (recursing(smm))
r = add_bop(smm, BOP_DEC, b, e);
else {
in(smm);
r = sm_ll_dec(&smm->ll, b, e, &nr_allocations);
r2 = out(smm);
}
return combine_errors(r, r2);
}
static int sm_metadata_new_block_(struct dm_space_map *sm, dm_block_t *b)
{
int r, r2 = 0;
int32_t nr_allocations;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
/*
* Any block we allocate has to be free in both the old and current ll.
*/
r = sm_ll_find_common_free_block(&smm->old_ll, &smm->ll, smm->begin, smm->ll.nr_blocks, b);
if (r == -ENOSPC) {
/*
* There's no free block between smm->begin and the end of the metadata device.
* We search before smm->begin in case something has been freed.
*/
r = sm_ll_find_common_free_block(&smm->old_ll, &smm->ll, 0, smm->begin, b);
}
if (r)
return r;
smm->begin = *b + 1;
if (recursing(smm))
r = add_bop(smm, BOP_INC, *b, *b + 1);
else {
in(smm);
r = sm_ll_inc(&smm->ll, *b, *b + 1, &nr_allocations);
r2 = out(smm);
}
if (!r)
smm->allocated_this_transaction++;
return combine_errors(r, r2);
}
static int sm_metadata_new_block(struct dm_space_map *sm, dm_block_t *b)
{
dm_block_t count;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
int r = sm_metadata_new_block_(sm, b);
if (r) {
DMERR_LIMIT("unable to allocate new metadata block");
return r;
}
r = sm_metadata_get_nr_free(sm, &count);
if (r) {
DMERR_LIMIT("couldn't get free block count");
return r;
}
check_threshold(&smm->threshold, count);
return r;
}
static int sm_metadata_commit(struct dm_space_map *sm)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
r = sm_ll_commit(&smm->ll);
if (r)
return r;
memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
smm->allocated_this_transaction = 0;
return 0;
}
static int sm_metadata_register_threshold_callback(struct dm_space_map *sm,
dm_block_t threshold,
dm_sm_threshold_fn fn,
void *context)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
set_threshold(&smm->threshold, threshold, fn, context);
return 0;
}
static int sm_metadata_root_size(struct dm_space_map *sm, size_t *result)
{
*result = sizeof(struct disk_sm_root);
return 0;
}
static int sm_metadata_copy_root(struct dm_space_map *sm, void *where_le, size_t max)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
struct disk_sm_root root_le;
root_le.nr_blocks = cpu_to_le64(smm->ll.nr_blocks);
root_le.nr_allocated = cpu_to_le64(smm->ll.nr_allocated);
root_le.bitmap_root = cpu_to_le64(smm->ll.bitmap_root);
root_le.ref_count_root = cpu_to_le64(smm->ll.ref_count_root);
if (max < sizeof(root_le))
return -ENOSPC;
memcpy(where_le, &root_le, sizeof(root_le));
return 0;
}
static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks);
static const struct dm_space_map ops = {
.destroy = sm_metadata_destroy,
.extend = sm_metadata_extend,
.get_nr_blocks = sm_metadata_get_nr_blocks,
.get_nr_free = sm_metadata_get_nr_free,
.get_count = sm_metadata_get_count,
.count_is_more_than_one = sm_metadata_count_is_more_than_one,
.set_count = sm_metadata_set_count,
.inc_blocks = sm_metadata_inc_blocks,
.dec_blocks = sm_metadata_dec_blocks,
.new_block = sm_metadata_new_block,
.commit = sm_metadata_commit,
.root_size = sm_metadata_root_size,
.copy_root = sm_metadata_copy_root,
.register_threshold_callback = sm_metadata_register_threshold_callback
};
/*----------------------------------------------------------------*/
/*
* When a new space map is created that manages its own space. We use
* this tiny bootstrap allocator.
*/
static void sm_bootstrap_destroy(struct dm_space_map *sm)
{
}
static int sm_bootstrap_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
DMERR("bootstrap doesn't support extend");
return -EINVAL;
}
static int sm_bootstrap_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
*count = smm->ll.nr_blocks;
return 0;
}
static int sm_bootstrap_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
*count = smm->ll.nr_blocks - smm->begin;
return 0;
}
static int sm_bootstrap_get_count(struct dm_space_map *sm, dm_block_t b,
uint32_t *result)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
*result = (b < smm->begin) ? 1 : 0;
return 0;
}
static int sm_bootstrap_count_is_more_than_one(struct dm_space_map *sm,
dm_block_t b, int *result)
{
*result = 0;
return 0;
}
static int sm_bootstrap_set_count(struct dm_space_map *sm, dm_block_t b,
uint32_t count)
{
DMERR("bootstrap doesn't support set_count");
return -EINVAL;
}
static int sm_bootstrap_new_block(struct dm_space_map *sm, dm_block_t *b)
{
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
/*
* We know the entire device is unused.
*/
if (smm->begin == smm->ll.nr_blocks)
return -ENOSPC;
*b = smm->begin++;
return 0;
}
static int sm_bootstrap_inc_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
r = add_bop(smm, BOP_INC, b, e);
if (r)
return r;
return 0;
}
static int sm_bootstrap_dec_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
r = add_bop(smm, BOP_DEC, b, e);
if (r)
return r;
return 0;
}
static int sm_bootstrap_commit(struct dm_space_map *sm)
{
return 0;
}
static int sm_bootstrap_root_size(struct dm_space_map *sm, size_t *result)
{
DMERR("bootstrap doesn't support root_size");
return -EINVAL;
}
static int sm_bootstrap_copy_root(struct dm_space_map *sm, void *where,
size_t max)
{
DMERR("bootstrap doesn't support copy_root");
return -EINVAL;
}
static const struct dm_space_map bootstrap_ops = {
.destroy = sm_bootstrap_destroy,
.extend = sm_bootstrap_extend,
.get_nr_blocks = sm_bootstrap_get_nr_blocks,
.get_nr_free = sm_bootstrap_get_nr_free,
.get_count = sm_bootstrap_get_count,
.count_is_more_than_one = sm_bootstrap_count_is_more_than_one,
.set_count = sm_bootstrap_set_count,
.inc_blocks = sm_bootstrap_inc_blocks,
.dec_blocks = sm_bootstrap_dec_blocks,
.new_block = sm_bootstrap_new_block,
.commit = sm_bootstrap_commit,
.root_size = sm_bootstrap_root_size,
.copy_root = sm_bootstrap_copy_root,
.register_threshold_callback = NULL
};
/*----------------------------------------------------------------*/
static int sm_metadata_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
dm_block_t old_len = smm->ll.nr_blocks;
/*
* Flick into a mode where all blocks get allocated in the new area.
*/
smm->begin = old_len;
memcpy(sm, &bootstrap_ops, sizeof(*sm));
/*
* Extend.
*/
r = sm_ll_extend(&smm->ll, extra_blocks);
if (r)
goto out;
/*
* We repeatedly increment then commit until the commit doesn't
* allocate any new blocks.
*/
do {
r = add_bop(smm, BOP_INC, old_len, smm->begin);
if (r)
goto out;
old_len = smm->begin;
r = apply_bops(smm);
if (r) {
DMERR("%s: apply_bops failed", __func__);
goto out;
}
r = sm_ll_commit(&smm->ll);
if (r)
goto out;
} while (old_len != smm->begin);
out:
/*
* Switch back to normal behaviour.
*/
memcpy(sm, &ops, sizeof(*sm));
return r;
}
/*----------------------------------------------------------------*/
struct dm_space_map *dm_sm_metadata_init(void)
{
struct sm_metadata *smm;
smm = kvmalloc_obj(*smm);
if (!smm)
return ERR_PTR(-ENOMEM);
memcpy(&smm->sm, &ops, sizeof(smm->sm));
return &smm->sm;
}
int dm_sm_metadata_create(struct dm_space_map *sm,
struct dm_transaction_manager *tm,
dm_block_t nr_blocks,
dm_block_t superblock)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
smm->begin = superblock + 1;
smm->recursion_count = 0;
smm->allocated_this_transaction = 0;
brb_init(&smm->uncommitted);
threshold_init(&smm->threshold);
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
r = sm_ll_new_metadata(&smm->ll, tm);
if (!r) {
if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
r = sm_ll_extend(&smm->ll, nr_blocks);
}
memcpy(&smm->sm, &ops, sizeof(smm->sm));
if (r)
return r;
/*
* Now we need to update the newly created data structures with the
* allocated blocks that they were built from.
*/
r = add_bop(smm, BOP_INC, superblock, smm->begin);
if (r)
return r;
r = apply_bops(smm);
if (r) {
DMERR("%s: apply_bops failed", __func__);
return r;
}
return sm_metadata_commit(sm);
}
int dm_sm_metadata_open(struct dm_space_map *sm,
struct dm_transaction_manager *tm,
void *root_le, size_t len)
{
int r;
struct sm_metadata *smm = container_of(sm, struct sm_metadata, sm);
r = sm_ll_open_metadata(&smm->ll, tm, root_le, len);
if (r)
return r;
smm->begin = 0;
smm->recursion_count = 0;
smm->allocated_this_transaction = 0;
brb_init(&smm->uncommitted);
threshold_init(&smm->threshold);
memcpy(&smm->old_ll, &smm->ll, sizeof(smm->old_ll));
return 0;
}
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef DM_SPACE_MAP_METADATA_H
#define DM_SPACE_MAP_METADATA_H
#include "dm-transaction-manager.h"
#define DM_SM_METADATA_BLOCK_SIZE (4096 >> SECTOR_SHIFT)
/*
* The metadata device is currently limited in size.
*
* We have one block of index, which can hold 255 index entries. Each
* index entry contains allocation info about ~16k metadata blocks.
*/
#define DM_SM_METADATA_MAX_BLOCKS (255 * ((1 << 14) - 64))
#define DM_SM_METADATA_MAX_SECTORS (DM_SM_METADATA_MAX_BLOCKS * DM_SM_METADATA_BLOCK_SIZE)
/*
* Unfortunately we have to use two-phase construction due to the cycle
* between the tm and sm.
*/
struct dm_space_map *dm_sm_metadata_init(void);
/*
* Create a fresh space map.
*/
int dm_sm_metadata_create(struct dm_space_map *sm,
struct dm_transaction_manager *tm,
dm_block_t nr_blocks,
dm_block_t superblock);
/*
* Open from a previously-recorded root.
*/
int dm_sm_metadata_open(struct dm_space_map *sm,
struct dm_transaction_manager *tm,
void *root_le, size_t len);
#endif /* DM_SPACE_MAP_METADATA_H */
@@ -0,0 +1,169 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_SPACE_MAP_H
#define _LINUX_DM_SPACE_MAP_H
#include "dm-block-manager.h"
typedef void (*dm_sm_threshold_fn)(void *context);
/*
* struct dm_space_map keeps a record of how many times each block in a device
* is referenced. It needs to be fixed on disk as part of the transaction.
*/
struct dm_space_map {
void (*destroy)(struct dm_space_map *sm);
/*
* You must commit before allocating the newly added space.
*/
int (*extend)(struct dm_space_map *sm, dm_block_t extra_blocks);
/*
* Extensions do not appear in this count until after commit has
* been called.
*/
int (*get_nr_blocks)(struct dm_space_map *sm, dm_block_t *count);
/*
* Space maps must never allocate a block from the previous
* transaction, in case we need to rollback. This complicates the
* semantics of get_nr_free(), it should return the number of blocks
* that are available for allocation _now_. For instance you may
* have blocks with a zero reference count that will not be
* available for allocation until after the next commit.
*/
int (*get_nr_free)(struct dm_space_map *sm, dm_block_t *count);
int (*get_count)(struct dm_space_map *sm, dm_block_t b, uint32_t *result);
int (*count_is_more_than_one)(struct dm_space_map *sm, dm_block_t b,
int *result);
int (*set_count)(struct dm_space_map *sm, dm_block_t b, uint32_t count);
int (*commit)(struct dm_space_map *sm);
int (*inc_blocks)(struct dm_space_map *sm, dm_block_t b, dm_block_t e);
int (*dec_blocks)(struct dm_space_map *sm, dm_block_t b, dm_block_t e);
/*
* new_block will increment the returned block.
*/
int (*new_block)(struct dm_space_map *sm, dm_block_t *b);
/*
* The root contains all the information needed to fix the space map.
* Generally this info is small, so squirrel it away in a disk block
* along with other info.
*/
int (*root_size)(struct dm_space_map *sm, size_t *result);
int (*copy_root)(struct dm_space_map *sm, void *copy_to_here_le, size_t len);
/*
* You can register one threshold callback which is edge-triggered
* when the free space in the space map drops below the threshold.
*/
int (*register_threshold_callback)(struct dm_space_map *sm,
dm_block_t threshold,
dm_sm_threshold_fn fn,
void *context);
};
/*----------------------------------------------------------------*/
static inline void dm_sm_destroy(struct dm_space_map *sm)
{
if (sm)
sm->destroy(sm);
}
static inline int dm_sm_extend(struct dm_space_map *sm, dm_block_t extra_blocks)
{
return sm->extend(sm, extra_blocks);
}
static inline int dm_sm_get_nr_blocks(struct dm_space_map *sm, dm_block_t *count)
{
return sm->get_nr_blocks(sm, count);
}
static inline int dm_sm_get_nr_free(struct dm_space_map *sm, dm_block_t *count)
{
return sm->get_nr_free(sm, count);
}
static inline int dm_sm_get_count(struct dm_space_map *sm, dm_block_t b,
uint32_t *result)
{
return sm->get_count(sm, b, result);
}
static inline int dm_sm_count_is_more_than_one(struct dm_space_map *sm,
dm_block_t b, int *result)
{
return sm->count_is_more_than_one(sm, b, result);
}
static inline int dm_sm_set_count(struct dm_space_map *sm, dm_block_t b,
uint32_t count)
{
return sm->set_count(sm, b, count);
}
static inline int dm_sm_commit(struct dm_space_map *sm)
{
return sm->commit(sm);
}
static inline int dm_sm_inc_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
return sm->inc_blocks(sm, b, e);
}
static inline int dm_sm_inc_block(struct dm_space_map *sm, dm_block_t b)
{
return dm_sm_inc_blocks(sm, b, b + 1);
}
static inline int dm_sm_dec_blocks(struct dm_space_map *sm, dm_block_t b, dm_block_t e)
{
return sm->dec_blocks(sm, b, e);
}
static inline int dm_sm_dec_block(struct dm_space_map *sm, dm_block_t b)
{
return dm_sm_dec_blocks(sm, b, b + 1);
}
static inline int dm_sm_new_block(struct dm_space_map *sm, dm_block_t *b)
{
return sm->new_block(sm, b);
}
static inline int dm_sm_root_size(struct dm_space_map *sm, size_t *result)
{
return sm->root_size(sm, result);
}
static inline int dm_sm_copy_root(struct dm_space_map *sm, void *copy_to_here_le, size_t len)
{
return sm->copy_root(sm, copy_to_here_le, len);
}
static inline int dm_sm_register_threshold_callback(struct dm_space_map *sm,
dm_block_t threshold,
dm_sm_threshold_fn fn,
void *context)
{
if (sm->register_threshold_callback)
return sm->register_threshold_callback(sm, threshold, fn, context);
return -EINVAL;
}
#endif /* _LINUX_DM_SPACE_MAP_H */
@@ -0,0 +1,541 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#include "dm-transaction-manager.h"
#include "dm-space-map.h"
#include "dm-space-map-disk.h"
#include "dm-space-map-metadata.h"
#include "dm-persistent-data-internal.h"
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/hash.h>
#include <linux/rbtree.h>
#include <linux/slab.h>
#include <linux/device-mapper.h>
#define DM_MSG_PREFIX "transaction manager"
/*----------------------------------------------------------------*/
#define PREFETCH_SIZE 128
#define PREFETCH_BITS 7
#define PREFETCH_SENTINEL ((dm_block_t) -1ULL)
struct prefetch_set {
struct mutex lock;
dm_block_t blocks[PREFETCH_SIZE];
};
static unsigned int prefetch_hash(dm_block_t b)
{
return hash_64(b, PREFETCH_BITS);
}
static void prefetch_wipe(struct prefetch_set *p)
{
unsigned int i;
for (i = 0; i < PREFETCH_SIZE; i++)
p->blocks[i] = PREFETCH_SENTINEL;
}
static void prefetch_init(struct prefetch_set *p)
{
mutex_init(&p->lock);
prefetch_wipe(p);
}
static void prefetch_add(struct prefetch_set *p, dm_block_t b)
{
unsigned int h = prefetch_hash(b);
mutex_lock(&p->lock);
if (p->blocks[h] == PREFETCH_SENTINEL)
p->blocks[h] = b;
mutex_unlock(&p->lock);
}
static void prefetch_issue(struct prefetch_set *p, struct dm_block_manager *bm)
{
unsigned int i;
mutex_lock(&p->lock);
for (i = 0; i < PREFETCH_SIZE; i++)
if (p->blocks[i] != PREFETCH_SENTINEL) {
dm_bm_prefetch(bm, p->blocks[i]);
p->blocks[i] = PREFETCH_SENTINEL;
}
mutex_unlock(&p->lock);
}
/*----------------------------------------------------------------*/
struct shadow_info {
struct rb_node node;
dm_block_t where;
};
/*
* It would be nice if we scaled with the size of transaction.
*/
#define DM_HASH_SIZE 256
#define DM_HASH_MASK (DM_HASH_SIZE - 1)
struct dm_transaction_manager {
int is_clone;
struct dm_transaction_manager *real;
struct dm_block_manager *bm;
struct dm_space_map *sm;
spinlock_t lock;
struct rb_root buckets[DM_HASH_SIZE];
struct prefetch_set prefetches;
};
/*----------------------------------------------------------------*/
static int is_shadow(struct dm_transaction_manager *tm, dm_block_t b)
{
int r = 0;
unsigned int bucket = dm_hash_block(b, DM_HASH_MASK);
struct rb_node **node;
spin_lock(&tm->lock);
node = &tm->buckets[bucket].rb_node;
while (*node) {
struct shadow_info *si =
rb_entry(*node, struct shadow_info, node);
if (b == si->where) {
r = 1;
break;
}
if (b < si->where)
node = &si->node.rb_left;
else
node = &si->node.rb_right;
}
spin_unlock(&tm->lock);
return r;
}
/*
* This can silently fail if there's no memory. We're ok with this since
* creating redundant shadows causes no harm.
*/
static void insert_shadow(struct dm_transaction_manager *tm, dm_block_t b)
{
unsigned int bucket;
struct shadow_info *si;
si = kmalloc_obj(*si, GFP_NOIO);
if (si) {
struct rb_node **node, *parent;
si->where = b;
bucket = dm_hash_block(b, DM_HASH_MASK);
spin_lock(&tm->lock);
node = &tm->buckets[bucket].rb_node;
parent = NULL;
while (*node) {
struct shadow_info *si =
rb_entry(*node, struct shadow_info, node);
parent = *node;
if (b < si->where)
node = &si->node.rb_left;
else
node = &si->node.rb_right;
}
rb_link_node(&si->node, parent, node);
rb_insert_color(&si->node, &tm->buckets[bucket]);
spin_unlock(&tm->lock);
}
}
static void wipe_shadow_table(struct dm_transaction_manager *tm)
{
unsigned int i;
spin_lock(&tm->lock);
for (i = 0; i < DM_HASH_SIZE; i++) {
while (!RB_EMPTY_ROOT(&tm->buckets[i])) {
struct shadow_info *si =
rb_entry(tm->buckets[i].rb_node, struct shadow_info, node);
rb_erase(&si->node, &tm->buckets[i]);
kfree(si);
}
}
spin_unlock(&tm->lock);
}
/*----------------------------------------------------------------*/
static struct dm_transaction_manager *dm_tm_create(struct dm_block_manager *bm,
struct dm_space_map *sm)
{
unsigned int i;
struct dm_transaction_manager *tm;
tm = kmalloc_obj(*tm);
if (!tm)
return ERR_PTR(-ENOMEM);
tm->is_clone = 0;
tm->real = NULL;
tm->bm = bm;
tm->sm = sm;
spin_lock_init(&tm->lock);
for (i = 0; i < DM_HASH_SIZE; i++)
tm->buckets[i] = RB_ROOT;
prefetch_init(&tm->prefetches);
return tm;
}
struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real)
{
struct dm_transaction_manager *tm;
tm = kmalloc_obj(*tm);
if (tm) {
tm->is_clone = 1;
tm->real = real;
}
return tm;
}
EXPORT_SYMBOL_GPL(dm_tm_create_non_blocking_clone);
void dm_tm_destroy(struct dm_transaction_manager *tm)
{
if (!tm)
return;
if (!tm->is_clone)
wipe_shadow_table(tm);
kfree(tm);
}
EXPORT_SYMBOL_GPL(dm_tm_destroy);
int dm_tm_pre_commit(struct dm_transaction_manager *tm)
{
int r;
if (tm->is_clone)
return -EWOULDBLOCK;
r = dm_sm_commit(tm->sm);
if (r < 0)
return r;
return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_pre_commit);
int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *root)
{
if (tm->is_clone)
return -EWOULDBLOCK;
wipe_shadow_table(tm);
dm_bm_unlock(root);
return dm_bm_flush(tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_commit);
int dm_tm_new_block(struct dm_transaction_manager *tm,
const struct dm_block_validator *v,
struct dm_block **result)
{
int r;
dm_block_t new_block;
if (tm->is_clone)
return -EWOULDBLOCK;
r = dm_sm_new_block(tm->sm, &new_block);
if (r < 0)
return r;
r = dm_bm_write_lock_zero(tm->bm, new_block, v, result);
if (r < 0) {
dm_sm_dec_block(tm->sm, new_block);
return r;
}
/*
* New blocks count as shadows in that they don't need to be
* shadowed again.
*/
insert_shadow(tm, new_block);
return 0;
}
static int __shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
const struct dm_block_validator *v,
struct dm_block **result)
{
int r;
dm_block_t new;
struct dm_block *orig_block;
r = dm_sm_new_block(tm->sm, &new);
if (r < 0)
return r;
r = dm_sm_dec_block(tm->sm, orig);
if (r < 0)
return r;
r = dm_bm_read_lock(tm->bm, orig, v, &orig_block);
if (r < 0)
return r;
/*
* It would be tempting to use dm_bm_unlock_move here, but some
* code, such as the space maps, keeps using the old data structures
* secure in the knowledge they won't be changed until the next
* transaction. Using unlock_move would force a synchronous read
* since the old block would no longer be in the cache.
*/
r = dm_bm_write_lock_zero(tm->bm, new, v, result);
if (r) {
dm_bm_unlock(orig_block);
return r;
}
memcpy(dm_block_data(*result), dm_block_data(orig_block),
dm_bm_block_size(tm->bm));
dm_bm_unlock(orig_block);
return r;
}
int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
const struct dm_block_validator *v, struct dm_block **result,
int *inc_children)
{
int r;
if (tm->is_clone)
return -EWOULDBLOCK;
r = dm_sm_count_is_more_than_one(tm->sm, orig, inc_children);
if (r < 0)
return r;
if (is_shadow(tm, orig) && !*inc_children)
return dm_bm_write_lock(tm->bm, orig, v, result);
r = __shadow_block(tm, orig, v, result);
if (r < 0)
return r;
insert_shadow(tm, dm_block_location(*result));
return r;
}
EXPORT_SYMBOL_GPL(dm_tm_shadow_block);
int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **blk)
{
if (tm->is_clone) {
int r = dm_bm_read_try_lock(tm->real->bm, b, v, blk);
if (r == -EWOULDBLOCK)
prefetch_add(&tm->real->prefetches, b);
return r;
}
return dm_bm_read_lock(tm->bm, b, v, blk);
}
EXPORT_SYMBOL_GPL(dm_tm_read_lock);
void dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b)
{
dm_bm_unlock(b);
}
EXPORT_SYMBOL_GPL(dm_tm_unlock);
void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b)
{
/*
* The non-blocking clone doesn't support this.
*/
BUG_ON(tm->is_clone);
dm_sm_inc_block(tm->sm, b);
}
EXPORT_SYMBOL_GPL(dm_tm_inc);
void dm_tm_inc_range(struct dm_transaction_manager *tm, dm_block_t b, dm_block_t e)
{
/*
* The non-blocking clone doesn't support this.
*/
BUG_ON(tm->is_clone);
dm_sm_inc_blocks(tm->sm, b, e);
}
EXPORT_SYMBOL_GPL(dm_tm_inc_range);
void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b)
{
/*
* The non-blocking clone doesn't support this.
*/
BUG_ON(tm->is_clone);
dm_sm_dec_block(tm->sm, b);
}
EXPORT_SYMBOL_GPL(dm_tm_dec);
void dm_tm_dec_range(struct dm_transaction_manager *tm, dm_block_t b, dm_block_t e)
{
/*
* The non-blocking clone doesn't support this.
*/
BUG_ON(tm->is_clone);
dm_sm_dec_blocks(tm->sm, b, e);
}
EXPORT_SYMBOL_GPL(dm_tm_dec_range);
void dm_tm_with_runs(struct dm_transaction_manager *tm,
const __le64 *value_le, unsigned int count, dm_tm_run_fn fn)
{
uint64_t b, begin, end;
bool in_run = false;
unsigned int i;
for (i = 0; i < count; i++, value_le++) {
b = le64_to_cpu(*value_le);
if (in_run) {
if (b == end)
end++;
else {
fn(tm, begin, end);
begin = b;
end = b + 1;
}
} else {
in_run = true;
begin = b;
end = b + 1;
}
}
if (in_run)
fn(tm, begin, end);
}
EXPORT_SYMBOL_GPL(dm_tm_with_runs);
int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b,
uint32_t *result)
{
if (tm->is_clone)
return -EWOULDBLOCK;
return dm_sm_get_count(tm->sm, b, result);
}
int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b,
int *result)
{
if (tm->is_clone)
return -EWOULDBLOCK;
return dm_sm_count_is_more_than_one(tm->sm, b, result);
}
struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm)
{
return tm->bm;
}
void dm_tm_issue_prefetches(struct dm_transaction_manager *tm)
{
prefetch_issue(&tm->prefetches, tm->bm);
}
EXPORT_SYMBOL_GPL(dm_tm_issue_prefetches);
/*----------------------------------------------------------------*/
static int dm_tm_create_internal(struct dm_block_manager *bm,
dm_block_t sb_location,
struct dm_transaction_manager **tm,
struct dm_space_map **sm,
int create,
void *sm_root, size_t sm_len)
{
int r;
*sm = dm_sm_metadata_init();
if (IS_ERR(*sm))
return PTR_ERR(*sm);
*tm = dm_tm_create(bm, *sm);
if (IS_ERR(*tm)) {
dm_sm_destroy(*sm);
return PTR_ERR(*tm);
}
if (create) {
r = dm_sm_metadata_create(*sm, *tm, dm_bm_nr_blocks(bm),
sb_location);
if (r) {
DMERR("couldn't create metadata space map");
goto bad;
}
} else {
r = dm_sm_metadata_open(*sm, *tm, sm_root, sm_len);
if (r) {
DMERR("couldn't open metadata space map");
goto bad;
}
}
return 0;
bad:
dm_tm_destroy(*tm);
dm_sm_destroy(*sm);
return r;
}
int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
struct dm_transaction_manager **tm,
struct dm_space_map **sm)
{
return dm_tm_create_internal(bm, sb_location, tm, sm, 1, NULL, 0);
}
EXPORT_SYMBOL_GPL(dm_tm_create_with_sm);
int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
void *sm_root, size_t root_len,
struct dm_transaction_manager **tm,
struct dm_space_map **sm)
{
return dm_tm_create_internal(bm, sb_location, tm, sm, 0, sm_root, root_len);
}
EXPORT_SYMBOL_GPL(dm_tm_open_with_sm);
/*----------------------------------------------------------------*/
@@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 Red Hat, Inc.
*
* This file is released under the GPL.
*/
#ifndef _LINUX_DM_TRANSACTION_MANAGER_H
#define _LINUX_DM_TRANSACTION_MANAGER_H
#include "dm-block-manager.h"
struct dm_transaction_manager;
struct dm_space_map;
/*----------------------------------------------------------------*/
/*
* This manages the scope of a transaction. It also enforces immutability
* of the on-disk data structures by limiting access to writeable blocks.
*
* Clients should not fiddle with the block manager directly.
*/
void dm_tm_destroy(struct dm_transaction_manager *tm);
/*
* The non-blocking version of a transaction manager is intended for use in
* fast path code that needs to do lookups e.g. a dm mapping function.
* You create the non-blocking variant from a normal tm. The interface is
* the same, except that most functions will just return -EWOULDBLOCK.
* Methods that return void yet may block should not be called on a clone
* viz. dm_tm_inc, dm_tm_dec. Call dm_tm_destroy() as you would with a normal
* tm when you've finished with it. You may not destroy the original prior
* to clones.
*/
struct dm_transaction_manager *dm_tm_create_non_blocking_clone(struct dm_transaction_manager *real);
/*
* We use a 2-phase commit here.
*
* i) Make all changes for the transaction *except* for the superblock.
* Then call dm_tm_pre_commit() to flush them to disk.
*
* ii) Lock your superblock. Update. Then call dm_tm_commit() which will
* unlock the superblock and flush it. No other blocks should be updated
* during this period. Care should be taken to never unlock a partially
* updated superblock; perform any operations that could fail *before* you
* take the superblock lock.
*/
int dm_tm_pre_commit(struct dm_transaction_manager *tm);
int dm_tm_commit(struct dm_transaction_manager *tm, struct dm_block *superblock);
/*
* These methods are the only way to get hold of a writeable block.
*/
/*
* dm_tm_new_block() is pretty self-explanatory. Make sure you do actually
* write to the whole of @data before you unlock, otherwise you could get
* a data leak. (The other option is for tm_new_block() to zero new blocks
* before handing them out, which will be redundant in most, if not all,
* cases).
* Zeroes the new block and returns with write lock held.
*/
int dm_tm_new_block(struct dm_transaction_manager *tm,
const struct dm_block_validator *v,
struct dm_block **result);
/*
* dm_tm_shadow_block() allocates a new block and copies the data from @orig
* to it. It then decrements the reference count on original block. Use
* this to update the contents of a block in a data structure, don't
* confuse this with a clone - you shouldn't access the orig block after
* this operation. Because the tm knows the scope of the transaction it
* can optimise requests for a shadow of a shadow to a no-op. Don't forget
* to unlock when you've finished with the shadow.
*
* The @inc_children flag is used to tell the caller whether it needs to
* adjust reference counts for children. (Data in the block may refer to
* other blocks.)
*
* Shadowing implicitly drops a reference on @orig so you must not have
* it locked when you call this.
*/
int dm_tm_shadow_block(struct dm_transaction_manager *tm, dm_block_t orig,
const struct dm_block_validator *v,
struct dm_block **result, int *inc_children);
/*
* Read access. You can lock any block you want. If there's a write lock
* on it outstanding then it'll block.
*/
int dm_tm_read_lock(struct dm_transaction_manager *tm, dm_block_t b,
const struct dm_block_validator *v,
struct dm_block **result);
void dm_tm_unlock(struct dm_transaction_manager *tm, struct dm_block *b);
/*
* Functions for altering the reference count of a block directly.
*/
void dm_tm_inc(struct dm_transaction_manager *tm, dm_block_t b);
void dm_tm_inc_range(struct dm_transaction_manager *tm, dm_block_t b, dm_block_t e);
void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b);
void dm_tm_dec_range(struct dm_transaction_manager *tm, dm_block_t b, dm_block_t e);
/*
* Builds up runs of adjacent blocks, and then calls the given fn
* (typically dm_tm_inc/dec). Very useful when you have to perform
* the same tm operation on all values in a btree leaf.
*/
typedef void (*dm_tm_run_fn)(struct dm_transaction_manager *, dm_block_t, dm_block_t);
void dm_tm_with_runs(struct dm_transaction_manager *tm,
const __le64 *value_le, unsigned int count, dm_tm_run_fn fn);
int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, uint32_t *result);
/*
* Finds out if a given block is shared (ie. has a reference count higher
* than one).
*/
int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b,
int *result);
struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm);
/*
* If you're using a non-blocking clone the tm will build up a list of
* requested blocks that weren't in core. This call will request those
* blocks to be prefetched.
*/
void dm_tm_issue_prefetches(struct dm_transaction_manager *tm);
/*
* A little utility that ties the knot by producing a transaction manager
* that has a space map managed by the transaction manager...
*
* Returns a tm that has an open transaction to write the new disk sm.
* Caller should store the new sm root and commit.
*
* The superblock location is passed so the metadata space map knows it
* shouldn't be used.
*/
int dm_tm_create_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
struct dm_transaction_manager **tm,
struct dm_space_map **sm);
int dm_tm_open_with_sm(struct dm_block_manager *bm, dm_block_t sb_location,
void *sm_root, size_t root_len,
struct dm_transaction_manager **tm,
struct dm_space_map **sm);
#endif /* _LINUX_DM_TRANSACTION_MANAGER_H */