scoutfs: remove generic extents storage

We are no longer storing individual extents in items from multiple
places and indexed in multiple ways.  We can remove this extent support
code.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2019-12-11 10:05:47 -08:00
committed by Zach Brown
parent dee9fbcf66
commit 0de6cade19
2 changed files with 0 additions and 422 deletions

View File

@@ -1,328 +0,0 @@
/*
* Copyright (C) 2018 Versity Software, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/fs.h>
#include "extents.h"
#include "counters.h"
#include "scoutfs_trace.h"
#include "msg.h"
/*
* These low level functions take on the fiddly details of extent
* manipulation. Callers handle serialization and storage and call in
* here to add or remove extents. This slices and dices the extents
* while dodging all the fence posts.
*/
/* return the last logical position that is in the extent, inclusive */
static u64 extent_end(struct scoutfs_extent *ext)
{
return ext->start + ext->len - 1;
}
/* returns true if the two extents overlap */
static bool extents_overlap(struct scoutfs_extent *a, struct scoutfs_extent *b)
{
return extent_end(a) >= b->start && a->start <= extent_end(b);
}
/* Returns true if a is entirely within b */
static bool extent_within(struct scoutfs_extent *a, struct scoutfs_extent *b)
{
return a->start >= b->start && extent_end(a) <= extent_end(b);
}
/*
* Returns true if two extents can be merged because they're adjacent,
* mapping is equally set or not, mappings are adjacent if they're set,
* and all the rest of the fields match.
*/
static bool extents_can_merge(struct scoutfs_extent *a,
struct scoutfs_extent *b)
{
if (a->start > b->start)
swap(a, b);
return (a->owner == b->owner) &&
((a->start + a->len) == b->start) &&
(!!a->map == !!b->map) &&
(!a->map || ((a->map + a->len) == b->map)) &&
(a->type == b->type) &&
(a->flags == b->flags);
}
int scoutfs_extent_init(struct scoutfs_extent *ext, u8 type, u64 owner,
u64 start, u64 len, u64 map, u8 flags)
{
/* don't allow 0 len or len wrapping map or start */
if ((start + len <= start) || (map + len <= map))
return -EIO;
ext->owner = owner;
ext->start = start;
ext->len = len;
ext->map = map;
ext->type = type;
ext->flags = flags;
return 0;
}
/*
* Returns true if the two extents intersect and modifies a to be the
* intersection of the two extents. Callers only need to initialize a's
* start and len when probing for an intersection and we'll copy the
* rest from b.
*/
bool scoutfs_extent_intersection(struct scoutfs_extent *a,
struct scoutfs_extent *b)
{
u64 new_start;
u64 new_end;
if (extents_overlap(a, b)) {
new_end = min(extent_end(a), extent_end(b));
new_start = max(a->start, b->start);
a->owner = b->owner;
a->start = new_start;
a->len = new_end - new_start + 1;
a->map = b->map ? (new_start - b->start) + b->map: 0;
a->type = b->type;
a->flags = b->flags;
return true;
}
return false;
}
static int extent_insert(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ins, void *data)
{
scoutfs_inc_counter(sb, extent_insert);
trace_scoutfs_extent_insert(sb, ins);
return iof(sb, SEI_INSERT, ins, data);
}
static int extent_delete(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *del, void *data)
{
scoutfs_inc_counter(sb, extent_delete);
trace_scoutfs_extent_delete(sb, del);
return iof(sb, SEI_DELETE, del, data);
}
/*
* Find the next extent using the given extent as the starting search
* position. This just passes the extent through to the underlying key
* building and searching routines.
*
* Callers have to be very careful when building the search extent.
* Most extents are indexed by their final logical position and some
* have all the metadata in the key. So a typical pattern is to search
* for an intersection by searching from a single block extent with the
* rest of the fields set to zero.
*
* But some callers are searching indexes of free extents where both the
* length and start are meaningful.
*
* The io function is responsible for ensuring that we return next
* extents with the same type and owner as the given extent.
*/
int scoutfs_extent_next(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ext, void *data)
{
int ret;
scoutfs_inc_counter(sb, extent_next);
trace_scoutfs_extent_next_input(sb, ext);
ret = iof(sb, SEI_NEXT, ext, data);
if (ret == 0)
trace_scoutfs_extent_next_output(sb, ext);
return ret;
}
int scoutfs_extent_prev(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ext, void *data)
{
int ret;
scoutfs_inc_counter(sb, extent_prev);
trace_scoutfs_extent_prev_input(sb, ext);
ret = iof(sb, SEI_PREV, ext, data);
if (ret == 0)
trace_scoutfs_extent_prev_output(sb, ext);
return ret;
}
/*
* Search for a next extent and see if we can merge it with the caller's
* extent. The caller has initialized next for us to search from. If
* we can merge then we update the callers extent, delete the old
* extent, and return 1. If we return an error or 0 then nothing will
* have changed.
*/
static int try_merge_next(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ext,
struct scoutfs_extent *next, void *data)
{
int ret;
ret = scoutfs_extent_next(sb, iof, next, data);
if (ret < 0) {
if (ret == -ENOENT)
ret = 0;
goto out;
}
if (extents_overlap(ext, next)) {
ret = -EIO;
goto out;
}
if (!extents_can_merge(ext, next)) {
ret = 0;
goto out;
}
if (next->start < ext->start) {
ext->start = next->start;
ext->map = next->map;
ext->len += next->len;
} else {
ext->len += next->len;
}
ret = extent_delete(sb, iof, next, data);
if (ret == 0)
ret = 1;
out:
return ret;
}
/*
* Add a new extent. It can not overlap with any existing extents. It
* may be merged with neighbouring extents.
*/
int scoutfs_extent_add(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *add, void *data)
{
struct scoutfs_extent right;
struct scoutfs_extent left;
struct scoutfs_extent ext;
bool ins_left = false;
bool ins_right = false;
int ret;
scoutfs_inc_counter(sb, extent_add);
trace_scoutfs_extent_add(sb, add);
ext = *add;
/* see if we are merging with and deleting a left neighbour */
if (ext.start) {
scoutfs_extent_init(&left, ext.type, ext.owner,
ext.start - 1, 1, 0, 0);
ret = try_merge_next(sb, iof, &ext, &left, data);
if (ret < 0)
goto out;
if (ret > 0)
ins_left = true;
}
/* see if we are merging with and deleting a right neighbour */
if (ext.start + ext.len <= SCOUTFS_BLOCK_MAX) {
scoutfs_extent_init(&right, ext.type, ext.owner,
ext.start, 1, 0, 0);
ret = try_merge_next(sb, iof, &ext, &right, data);
if (ret < 0)
goto out;
if (ret > 0)
ins_right = true;
}
/* finally insert our new (possibly merged) extent */
ret = extent_insert(sb, iof, &ext, data);
out:
scoutfs_extent_cleanup(ret < 0 && ins_right, extent_insert, sb, iof,
&right, data, SC_EXTENT_ADD_CLEANUP,
corrupt_extent_add_cleanup, add);
scoutfs_extent_cleanup(ret < 0 && ins_left, extent_insert, sb, iof,
&left, data, SC_EXTENT_ADD_CLEANUP,
corrupt_extent_add_cleanup, add);
return ret;
}
/*
* Remove a region of an existing extent. The region to remove must be
* be fully within an existing extent. This creates the items left
* behind on either end of the removed region as appropriate.
*/
int scoutfs_extent_remove(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *rem, void *data)
{
struct scoutfs_extent right;
struct scoutfs_extent left;
struct scoutfs_extent ext;
bool ins_ext = false;
bool del_left = false;
int ret;
scoutfs_inc_counter(sb, extent_remove);
trace_scoutfs_extent_remove(sb, rem);
scoutfs_extent_init(&ext, rem->type, rem->owner, rem->start, 1, 0, 0);
ret = scoutfs_extent_next(sb, iof, &ext, data);
if (ret < 0)
goto out;
/* make sure they're correct */
if (!extent_within(rem, &ext)) {
ret = -EIO;
goto out;
}
ret = extent_delete(sb, iof, &ext, data);
if (ret)
goto out;
ins_ext = true;
if (rem->start != ext.start) {
scoutfs_extent_init(&left, ext.type, ext.owner,
ext.start, rem->start - ext.start,
ext.map, ext.flags);
ret = extent_insert(sb, iof, &left, data);
if (ret)
goto out;
del_left = true;
}
if (extent_end(rem) != extent_end(&ext)) {
scoutfs_extent_init(&right, ext.type, ext.owner,
rem->start + rem->len,
extent_end(&ext) - extent_end(rem),
ext.map ? rem->map + rem->len : 0,
ext.flags);
ret = extent_insert(sb, iof, &right, data);
}
out:
scoutfs_extent_cleanup(ret < 0 && del_left, extent_delete, sb, iof,
&left, data, SC_EXTENT_REM_CLEANUP,
corrupt_extent_rem_cleanup, rem);
scoutfs_extent_cleanup(ret < 0 && ins_ext, extent_insert, sb, iof,
&ext, data, SC_EXTENT_REM_CLEANUP,
corrupt_extent_rem_cleanup, rem);
return ret;
}

View File

@@ -1,94 +0,0 @@
#ifndef _SCOUTFS_EXTENTS_H_
#define _SCOUTFS_EXTENTS_H_
/*
* Native storage for an extent. Read and write translates between
* these and persistent storage.
*/
struct scoutfs_extent {
u64 owner;
u64 start;
u64 len;
u64 map;
u8 type;
u8 flags;
};
#define SE_FMT "%llu.%llu.%llu.%llu.%u.%x"
#define SE_ARG(ext) (ext)->owner, (ext)->start, (ext)->len, (ext)->map, \
(ext)->type, (ext)->flags
#define se_trace_define(name) \
__field(__u64, name##_owner) \
__field(__u64, name##_start) \
__field(__u64, name##_len) \
__field(__u64, name##_map) \
__field(__u8, name##_type) \
__field(__u8, name##_flags)
/* doesn't support null extent pointers */
#define se_trace_assign(name, ext) \
do { \
__typeof__(ext) _ext = (ext); \
\
__entry->name##_owner = _ext->owner; \
__entry->name##_start = _ext->start; \
__entry->name##_len = _ext->len; \
__entry->name##_map = _ext->map; \
__entry->name##_type = _ext->type; \
__entry->name##_flags = _ext->flags; \
} while (0)
#define se_trace_args(name) \
__entry->name##_owner, __entry->name##_start, __entry->name##_len, \
__entry->name##_map, __entry->name##_type, __entry->name##_flags
enum {
SEI_NEXT,
SEI_PREV,
SEI_INSERT,
SEI_DELETE,
};
typedef int (*scoutfs_extent_io_t)(struct super_block *sb, int op,
struct scoutfs_extent *ext, void *data);
int scoutfs_extent_init(struct scoutfs_extent *ext, u8 type, u64 owner,
u64 start, u64 len, u64 map, u8 flags);
bool scoutfs_extent_intersection(struct scoutfs_extent *a,
struct scoutfs_extent *b);
int scoutfs_extent_next(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ext, void *data);
int scoutfs_extent_prev(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *ext, void *data);
int scoutfs_extent_add(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *add, void *data);
int scoutfs_extent_remove(struct super_block *sb, scoutfs_extent_io_t iof,
struct scoutfs_extent *rem, void *data);
/*
* The process of modifying an extent creates and deletes many
* intermediate extents. If we hit an error we need to undo the
* process. If we then hit an error we can be left with inconsistent
* extent items.
*
* We could fix this for extents that are stored in the item cache
* because it has tools for ensuring that operations can't fail.
* Extents that are stored in the btree currently can't avoid errors.
* We'd have to predirty blocks, allow deletion to fall below thresholds
* if merging saw an error, and preallocate blocks to be used for
* splitting/growth. It'd probably be worth it.
*/
#define scoutfs_extent_cleanup(cond, ext_func, sb, iof, clean, data, \
which, ctr, ext) \
do { \
__typeof__(sb) _sb = (sb); \
int _ret; \
\
if ((cond) && (_ret = ext_func(_sb, iof, clean, data)) < 0) \
scoutfs_corruption(_sb, which, ctr, \
"ext "SE_FMT" clean "SE_FMT" ret %d", \
SE_ARG(ext), SE_ARG(clean), _ret); \
} while (0)
#endif