scoutfs: use packed extents and bitmaps

The btree forest item storage doesn't have as much item granular state
as the item cache did.  The item cache could tell if a cached item was
populated from persistent storage or was created in memory.  It could
simply remove created items rather than leaving behind a deletion item.

The cached btree blocks in the btree forest item storage mechanism can't
do this.  It has to create deletion items when deleting newly created
items because it doesn't know if the item already exists in the
persistent record or not.

This created a problem with the extent storage we were using.  The
individual extent items were stored with a key set to the last logical
block of their extent.  As extents grew or shrank they often were
deleted and created at different key values during a transaction.  In
the btree forest log trees this left a huge stream of deletion items
beind, one for every previous version of the extent.  Then searches for
an extent covering a block would have to skip over all these deleted
items before hitting the current stored extent.

Streaming writes would operate on O(n) for every extent operation.  It
got to be out of hand.  This large change solves the problem by using
more coarse and stable item storage to track free blocks and blocks
mapped into file data.

For file data we now have large packed extent items which store packed
representations of all the logical mappings of a fixed region of a file.
The data code has loading and storage functions which transfer that
persistent version to and from the version that is modified in memory.

Free blocks are stored in bitmaps that are similarly efficiently packed
into fixed size items.  The client is no longer working with free extent
items managed by the forest, it's working with free block bitmap btrees
directly.  It needs access to the client's metadata block allocator and
block write contexts so we move those two out of the forest code and up
into the transaction.

Previously the client and server would exchange extents with network
messages.  Now the roots of the btrees that store the free block bitmap
items are communicated along with the roots of the other trees involved
in a transaction.  The client doesn't need to send free extents back to
the server so we can remove those tasks and rpcs.

The server no longer has to manage free extents.  It transfers block
bitmap items between trees around commits.   All of its extent
manipulation can be removed.

The item size portion of transaction item counts are removed because
we're not using that level of granularity now that metadata transactions
are dirty btree blocks instead of dirty items we pack into fixed sized
segments.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2019-11-08 10:24:44 -08:00
committed by Zach Brown
parent 986e66d6c6
commit dee9fbcf66
17 changed files with 2081 additions and 1538 deletions
-1
View File
@@ -17,7 +17,6 @@ scoutfs-y += \
data.o \
dir.o \
export.o \
extents.o \
file.o \
forest.o \
inode.o \
-42
View File
@@ -88,48 +88,6 @@ int scoutfs_client_alloc_inodes(struct super_block *sb, u64 count,
return ret;
}
/*
* Ask the server for an extent of at most @blocks blocks. It can return
* smaller extents.
*/
int scoutfs_client_alloc_extent(struct super_block *sb, u64 blocks, u64 *start,
u64 *len)
{
struct client_info *client = SCOUTFS_SB(sb)->client_info;
__le64 leblocks = cpu_to_le64(blocks);
struct scoutfs_net_extent nex;
int ret;
ret = scoutfs_net_sync_request(sb, client->conn,
SCOUTFS_NET_CMD_ALLOC_EXTENT,
&leblocks, sizeof(leblocks),
&nex, sizeof(nex));
if (ret == 0) {
if (nex.len == 0) {
ret = -ENOSPC;
} else {
*start = le64_to_cpu(nex.start);
*len = le64_to_cpu(nex.len);
}
}
return ret;
}
int scoutfs_client_free_extents(struct super_block *sb,
struct scoutfs_net_extent_list *nexl)
{
struct client_info *client = SCOUTFS_SB(sb)->client_info;
unsigned int bytes;
bytes = SCOUTFS_NET_EXTENT_LIST_BYTES(le64_to_cpu(nexl->nr));
return scoutfs_net_sync_request(sb, client->conn,
SCOUTFS_NET_CMD_FREE_EXTENTS,
nexl, bytes, NULL, 0);
}
int scoutfs_client_get_log_trees(struct super_block *sb,
struct scoutfs_log_trees *lt)
{
-4
View File
@@ -3,10 +3,6 @@
int scoutfs_client_alloc_inodes(struct super_block *sb, u64 count,
u64 *ino, u64 *nr);
int scoutfs_client_alloc_extent(struct super_block *sb, u64 blocks, u64 *start,
u64 *len);
int scoutfs_client_free_extents(struct super_block *sb,
struct scoutfs_net_extent_list *nexl);
int scoutfs_client_get_log_trees(struct super_block *sb,
struct scoutfs_log_trees *lt);
int scoutfs_client_commit_log_trees(struct super_block *sb,
+3 -19
View File
@@ -252,7 +252,7 @@ static inline const struct scoutfs_item_count SIC_WRITE_BEGIN(void)
__count_dirty_inode(&cnt);
cnt.items += nr_free + nr_file;
cnt.vals += nr_file * sizeof(struct scoutfs_file_extent);
cnt.vals += nr_file;
return cnt;
}
@@ -276,22 +276,7 @@ SIC_TRUNC_EXTENT(struct inode *inode)
__count_dirty_inode(&cnt);
cnt.items += nr_file + nr_free;
cnt.vals += nr_file * sizeof(struct scoutfs_file_extent);
return cnt;
}
/*
* Returning extents to the server can, at most:
* - delete MAX_NR extents with indexed copies
* - create an extent for the leftovers of the last extent
*/
static inline const struct scoutfs_item_count SIC_RETURN_EXTENTS(void)
{
struct scoutfs_item_count cnt = {0,};
unsigned int nr = SCOUTFS_NET_EXTENT_LIST_MAX_NR + 1;
cnt.items += (nr * 2);
cnt.vals += nr_file;
return cnt;
}
@@ -312,7 +297,7 @@ static inline const struct scoutfs_item_count SIC_FALLOCATE_ONE(void)
__count_dirty_inode(&cnt);
cnt.items += nr_free + nr_file;
cnt.vals += nr_file * sizeof(struct scoutfs_file_extent);
cnt.vals += nr_file;
return cnt;
}
@@ -327,7 +312,6 @@ static inline const struct scoutfs_item_count SIC_SETATTR_MORE(void)
__count_dirty_inode(&cnt);
cnt.items++;
cnt.vals += sizeof(struct scoutfs_file_extent);
return cnt;
}
-5
View File
@@ -105,11 +105,6 @@
EXPAND_COUNTER(quorum_write_block) \
EXPAND_COUNTER(quorum_write_block_error) \
EXPAND_COUNTER(quorum_fenced) \
EXPAND_COUNTER(server_extent_alloc) \
EXPAND_COUNTER(server_extent_alloc_error) \
EXPAND_COUNTER(server_free_extent) \
EXPAND_COUNTER(server_free_pending_extent) \
EXPAND_COUNTER(server_free_pending_error) \
EXPAND_COUNTER(trans_commit_fsync) \
EXPAND_COUNTER(trans_commit_full) \
EXPAND_COUNTER(trans_commit_sync_fs) \
+1666 -780
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -36,8 +36,17 @@ struct scoutfs_data_wait {
.node.__rb_parent_color = (unsigned long)(&nm.node), \
}
struct scoutfs_traced_extent {
u64 iblock;
u64 count;
u64 blkno;
u8 flags;
};
extern const struct address_space_operations scoutfs_file_aops;
extern const struct file_operations scoutfs_file_fops;
struct scoutfs_balloc_allocator;
struct scoutfs_block_writer;
int scoutfs_data_truncate_items(struct super_block *sb, struct inode *inode,
u64 ino, u64 iblock, u64 last, bool offline,
@@ -63,6 +72,24 @@ int scoutfs_data_waiting(struct super_block *sb, u64 ino, u64 iblock,
struct scoutfs_ioctl_data_waiting_entry *dwe,
unsigned int nr);
int scoutfs_data_move_alloc_bits(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_balloc_root *dst,
struct scoutfs_balloc_root *src,
__le64 *cursor, u64 min_dst_total);
int scoutfs_data_add_free_blocks(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_balloc_root *broot,
u64 blkno, u64 count);
void scoutfs_data_init_btrees(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_log_trees *lt);
void scoutfs_data_get_btrees(struct super_block *sb,
struct scoutfs_log_trees *lt);
int scoutfs_data_setup(struct super_block *sb);
void scoutfs_data_destroy(struct super_block *sb);
+29 -58
View File
@@ -61,8 +61,8 @@
struct forest_info {
struct rw_semaphore rwsem;
struct scoutfs_balloc_allocator alloc;
struct scoutfs_block_writer wri;
struct scoutfs_balloc_allocator *alloc;
struct scoutfs_block_writer *wri;
struct scoutfs_log_trees our_log;
};
@@ -1025,14 +1025,14 @@ static int set_lock_bloom_bits(struct super_block *sb,
if (!ref->blkno || !scoutfs_block_writer_is_dirty(sb, bl)) {
ret = scoutfs_balloc_alloc(sb, &finf->alloc, &finf->wri,
ret = scoutfs_balloc_alloc(sb, finf->alloc, finf->wri,
&blkno);
if (ret < 0)
goto unlock;
new_bl = scoutfs_block_create(sb, blkno);
if (IS_ERR(new_bl)) {
err = scoutfs_balloc_free(sb, &finf->alloc, &finf->wri,
err = scoutfs_balloc_free(sb, finf->alloc, finf->wri,
blkno);
BUG_ON(err); /* could have dirtied */
ret = PTR_ERR(new_bl);
@@ -1040,7 +1040,7 @@ static int set_lock_bloom_bits(struct super_block *sb,
}
if (bl) {
err = scoutfs_balloc_free(sb, &finf->alloc, &finf->wri,
err = scoutfs_balloc_free(sb, finf->alloc, finf->wri,
le64_to_cpu(ref->blkno));
BUG_ON(err); /* could have dirtied */
memcpy(new_bl->data, bl->data, SCOUTFS_BLOCK_SIZE);
@@ -1048,7 +1048,7 @@ static int set_lock_bloom_bits(struct super_block *sb,
memset(new_bl->data, 0, SCOUTFS_BLOCK_SIZE);
}
scoutfs_block_writer_mark_dirty(sb, &finf->wri, new_bl);
scoutfs_block_writer_mark_dirty(sb, finf->wri, new_bl);
scoutfs_block_put(sb, bl);
bl = new_bl;
@@ -1165,7 +1165,7 @@ static int forest_insert(struct super_block *sb, struct scoutfs_key *key,
scoutfs_key_to_be(&kbe, key);
down_write(&finf->rwsem);
ret = scoutfs_btree_force(sb, &finf->alloc, &finf->wri,
ret = scoutfs_btree_force(sb, finf->alloc, finf->wri,
&finf->our_log.item_root, &kbe, sizeof(kbe),
iv->iov_base, iv->iov_len);
up_write(&finf->rwsem);
@@ -1249,7 +1249,7 @@ static int forest_delete(struct super_block *sb, struct scoutfs_key *key,
liv.flags = SCOUTFS_LOG_ITEM_FLAG_DELETION;
down_write(&finf->rwsem);
ret = scoutfs_btree_force(sb, &finf->alloc, &finf->wri,
ret = scoutfs_btree_force(sb, finf->alloc, finf->wri,
&finf->our_log.item_root,
&kbe, sizeof(kbe), &liv, sizeof(liv));
up_write(&finf->rwsem);
@@ -1318,69 +1318,41 @@ void scoutfs_forest_free_batch(struct super_block *sb, struct list_head *list)
/*
* This is called from transactions as a new transaction opens and is
* serialized with all writers. Get the tree roots that we'll need
* for the transaction.
* serialized with all writers.
*/
int scoutfs_forest_get_log_trees(struct super_block *sb)
void scoutfs_forest_init_btrees(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_log_trees *lt)
{
DECLARE_FOREST_INFO(sb, finf);
struct scoutfs_log_trees lt;
int ret;
ret = scoutfs_client_get_log_trees(sb, &lt);
if (ret)
goto out;
down_write(&finf->rwsem);
scoutfs_balloc_init(&finf->alloc, &lt.alloc_root, &lt.free_root);
scoutfs_block_writer_init(sb, &finf->wri);
finf->our_log = lt;
finf->alloc = alloc;
finf->wri = wri;
/* we use the item and bloom trees */
memset(&finf->our_log, 0, sizeof(finf->our_log));
finf->our_log.item_root = lt->item_root;
finf->our_log.bloom_ref = lt->bloom_ref;
up_write(&finf->rwsem);
ret = 0;
out:
return ret;
}
bool scoutfs_forest_has_dirty(struct super_block *sb)
{
DECLARE_FOREST_INFO(sb, finf);
return scoutfs_block_writer_has_dirty(sb, &finf->wri);
}
unsigned long scoutfs_forest_dirty_bytes(struct super_block *sb)
{
DECLARE_FOREST_INFO(sb, finf);
return scoutfs_block_writer_dirty_bytes(sb, &finf->wri);
}
int scoutfs_forest_write(struct super_block *sb)
{
DECLARE_FOREST_INFO(sb, finf);
return scoutfs_block_writer_write(sb, &finf->wri);
}
/*
* This is called during transaction commit which excludes forest writer
* calls. The caller has already written all the dirty blocks that the
* forest roots reference.
* forest roots reference. They're getting the roots to send to the server
* for the commit.
*/
int scoutfs_forest_commit(struct super_block *sb)
void scoutfs_forest_get_btrees(struct super_block *sb,
struct scoutfs_log_trees *lt)
{
DECLARE_FOREST_INFO(sb, finf);
struct scoutfs_log_trees lt = {
.alloc_root = finf->alloc.alloc_root,
.free_root = finf->alloc.free_root,
.item_root = finf->our_log.item_root,
.bloom_ref = finf->our_log.bloom_ref,
.rid = finf->our_log.rid,
.nr = finf->our_log.nr,
};
return scoutfs_client_commit_log_trees(sb, &lt);
lt->item_root = finf->our_log.item_root;
lt->bloom_ref = finf->our_log.bloom_ref;
}
int scoutfs_forest_setup(struct super_block *sb)
@@ -1395,7 +1367,7 @@ int scoutfs_forest_setup(struct super_block *sb)
goto out;
}
/* the finf fields will be setup as we open a transaction */
/* the finf fields will be setup as we open a transaction */
init_rwsem(&finf->rwsem);
sbi->forest_info = finf;
@@ -1413,7 +1385,6 @@ void scoutfs_forest_destroy(struct super_block *sb)
struct forest_info *finf = SCOUTFS_SB(sb)->forest_info;
if (finf) {
scoutfs_block_writer_forget_all(sb, &finf->wri);
kfree(finf);
sbi->forest_info = NULL;
}
+9 -5
View File
@@ -1,6 +1,9 @@
#ifndef _SCOUTFS_FOREST_H_
#define _SCOUTFS_FOREST_H_
struct scoutfs_balloc_allocator;
struct scoutfs_block_writer;
int scoutfs_forest_lookup(struct super_block *sb, struct scoutfs_key *key,
struct kvec *val, struct scoutfs_lock *lock);
int scoutfs_forest_lookup_exact(struct super_block *sb,
@@ -36,11 +39,12 @@ int scoutfs_forest_restore(struct super_block *sb, struct list_head *list,
struct scoutfs_lock *lock);
void scoutfs_forest_free_batch(struct super_block *sb, struct list_head *list);
int scoutfs_forest_get_log_trees(struct super_block *sb);
bool scoutfs_forest_has_dirty(struct super_block *sb);
unsigned long scoutfs_forest_dirty_bytes(struct super_block *sb);
int scoutfs_forest_write(struct super_block *sb);
int scoutfs_forest_commit(struct super_block *sb);
void scoutfs_forest_init_btrees(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_log_trees *lt);
void scoutfs_forest_get_btrees(struct super_block *sb,
struct scoutfs_log_trees *lt);
void scoutfs_forest_clear_lock(struct super_block *sb,
struct scoutfs_lock *lock);
+72 -49
View File
@@ -105,11 +105,6 @@ struct scoutfs_key {
#define skxi_ino _sk_second
#define skxi_id _sk_third
/* node free extent */
#define sknf_rid _sk_first
#define sknf_major _sk_second
#define sknf_minor _sk_third
/* node orphan inode */
#define sko_rid _sk_first
#define sko_ino _sk_second
@@ -132,9 +127,10 @@ struct scoutfs_key {
#define sks_ino _sk_first
#define sks_nr _sk_second
/* file extent */
#define skfe_ino _sk_first
#define skfe_last _sk_second
/* packed extents */
#define skpe_ino _sk_first
#define skpe_base _sk_second
#define skpe_part _sk_fourth
/*
* The btree still uses memcmp() to compare keys. We should fix that
@@ -235,18 +231,32 @@ struct scoutfs_balloc_item_val {
} __packed;
/*
* Free extents are stored in the server in an allocation btree. The
* type differentiates whether start or length is in stored in the major
* value and is the primary sort key. 'start' is set to the final block
* in the extent so that overlaping queries can be done with next
* instead prev.
* Free data blocks are tracked in bitmaps stored in btree items.
*/
struct scoutfs_extent_btree_key {
struct scoutfs_block_bitmap_key {
__u8 type;
__be64 major;
__be64 minor;
__be64 base;
} __packed;
#define SCOUTFS_BLOCK_BITMAP_BIG 0
#define SCOUTFS_BLOCK_BITMAP_LITTLE 1
#define SCOUTFS_PACKED_BITMAP_WORDS 32
#define SCOUTFS_PACKED_BITMAP_BITS (SCOUTFS_PACKED_BITMAP_WORDS * 64)
#define SCOUTFS_PACKED_BITMAP_MAX_BYTES \
offsetof(struct scoutfs_packed_bitmap, \
words[SCOUTFS_PACKED_BITMAP_WORDS])
#define SCOUTFS_BLOCK_BITMAP_BITS SCOUTFS_PACKED_BITMAP_BITS
#define SCOUTFS_BLOCK_BITMAP_BIT_MASK (SCOUTFS_PACKED_BITMAP_BITS - 1)
#define SCOUTFS_BLOCK_BITMAP_BASE_SHIFT (ilog2(SCOUTFS_PACKED_BITMAP_BITS))
struct scoutfs_packed_bitmap {
__le64 present;
__le64 set;
__le64 words[0];
};
/*
* The lock server keeps a persistent record of connected clients so that
* server failover knows who to wait for before resuming operations.
@@ -277,11 +287,18 @@ struct scoutfs_mounted_client_btree_val {
#define SCOUTFS_MOUNTED_CLIENT_VOTER (1 << 0)
/*
* XXX I imagine we should rename these now that they've evolved to track
* all the btrees that clients use during a transaction. It's not just
* about item logs, it's about clients making changes to trees.
*/
struct scoutfs_log_trees {
struct scoutfs_balloc_root alloc_root;
struct scoutfs_balloc_root free_root;
struct scoutfs_btree_root item_root;
struct scoutfs_btree_ref bloom_ref;
struct scoutfs_balloc_root data_alloc;
struct scoutfs_balloc_root data_free;
__le64 rid;
__le64 nr;
} __packed;
@@ -296,6 +313,8 @@ struct scoutfs_log_trees_val {
struct scoutfs_balloc_root free_root;
struct scoutfs_btree_root item_root;
struct scoutfs_btree_ref bloom_ref;
struct scoutfs_balloc_root data_alloc;
struct scoutfs_balloc_root data_free;
} __packed;
struct scoutfs_log_item_value {
@@ -351,9 +370,7 @@ struct scoutfs_bloom_block {
#define SCOUTFS_XATTR_INDEX_NAME_TYPE 1
/* rid zone (also used in server alloc btree) */
#define SCOUTFS_FREE_EXTENT_BLKNO_TYPE 1
#define SCOUTFS_FREE_EXTENT_BLOCKS_TYPE 2
#define SCOUTFS_ORPHAN_TYPE 3
#define SCOUTFS_ORPHAN_TYPE 1
/* fs zone */
#define SCOUTFS_INODE_TYPE 1
@@ -362,23 +379,45 @@ struct scoutfs_bloom_block {
#define SCOUTFS_READDIR_TYPE 4
#define SCOUTFS_LINK_BACKREF_TYPE 5
#define SCOUTFS_SYMLINK_TYPE 6
#define SCOUTFS_FILE_EXTENT_TYPE 7
#define SCOUTFS_PACKED_EXTENT_TYPE 7
/* lock zone, only ever found in lock ranges, never in persistent items */
#define SCOUTFS_RENAME_TYPE 1
#define SCOUTFS_MAX_TYPE 8 /* power of 2 is efficient */
/*
* File extents have more data than easily fits in the key so we move
* the non-indexed fields into the value.
* The extents that map blocks in a fixed-size logical region of a file
* are packed and stored in item values. The packed extents are
* contiguous so the starting logical block is implicit from the length
* of previous extents. Sparse regions are represented by 0 flags and
* blkno. The blkno of a packed extent is encoded as the zigzag (lsb is
* sign bit) difference from the last blkno of the previous extent.
* This guarantees that non-sparse extents must have a blkno delta of at
* least -1/1. High zero byte aren't stored.
*/
struct scoutfs_file_extent {
__le64 blkno;
__le64 len;
__u8 flags;
struct scoutfs_packed_extent {
__le16 count;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 diff_bytes:4,
flags:3,
final:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 final:1,
flags:3,
diff_bytes:4;
#else
#error "no {BIG,LITTLE}_ENDIAN_BITFIELD defined?"
#endif
__u8 le_blkno_diff[0];
} __packed;
#define SCOUTFS_PACKEXT_BLOCKS (8 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE)
#define SCOUTFS_PACKEXT_BASE_SHIFT (ilog2(SCOUTFS_PACKEXT_BLOCKS))
#define SCOUTFS_PACKEXT_BASE_MASK (~((__u64)SCOUTFS_PACKEXT_BLOCKS - 1))
#define SCOUTFS_PACKEXT_MAX_BYTES SCOUTFS_MAX_VAL_SIZE
#define SEF_OFFLINE (1 << 0)
#define SEF_UNWRITTEN (1 << 1)
#define SEF_UNKNOWN (U8_MAX << 2)
@@ -451,8 +490,12 @@ struct scoutfs_super_block {
__le64 next_ino;
__le64 next_trans_seq;
__le64 total_blocks;
__le64 next_uninit_free_block;
__le64 next_uninit_meta_blkno;
__le64 last_uninit_meta_blkno;
__le64 next_uninit_data_blkno;
__le64 last_uninit_data_blkno;
__le64 core_balloc_cursor;
__le64 core_data_alloc_cursor;
__le64 free_blocks;
__le64 first_fs_blkno;
__le64 last_fs_blkno;
@@ -463,7 +506,8 @@ struct scoutfs_super_block {
struct scoutfs_inet_addr server_addr;
struct scoutfs_balloc_root core_balloc_alloc;
struct scoutfs_balloc_root core_balloc_free;
struct scoutfs_btree_root alloc_root;
struct scoutfs_balloc_root core_data_alloc;
struct scoutfs_balloc_root core_data_free;
struct scoutfs_btree_root fs_root;
struct scoutfs_btree_root logs_root;
struct scoutfs_btree_root lock_clients;
@@ -653,8 +697,6 @@ struct scoutfs_net_header {
enum {
SCOUTFS_NET_CMD_GREETING = 0,
SCOUTFS_NET_CMD_ALLOC_INODES,
SCOUTFS_NET_CMD_ALLOC_EXTENT,
SCOUTFS_NET_CMD_FREE_EXTENTS,
SCOUTFS_NET_CMD_GET_LOG_TREES,
SCOUTFS_NET_CMD_COMMIT_LOG_TREES,
SCOUTFS_NET_CMD_ADVANCE_SEQ,
@@ -705,25 +747,6 @@ struct scoutfs_net_statfs {
__u8 uuid[SCOUTFS_UUID_BYTES]; /* logical volume uuid */
} __packed;
struct scoutfs_net_extent {
__le64 start;
__le64 len;
} __packed;
struct scoutfs_net_extent_list {
__le64 nr;
struct {
__le64 start;
__le64 len;
} __packed extents[0];
} __packed;
#define SCOUTFS_NET_EXTENT_LIST_BYTES(nr) \
offsetof(struct scoutfs_net_extent_list, extents[nr])
/* arbitrarily makes a nice ~1k extent list payload */
#define SCOUTFS_NET_EXTENT_LIST_MAX_NR 64
struct scoutfs_net_lock {
struct scoutfs_key key;
__le64 write_version;
+7 -7
View File
@@ -587,6 +587,13 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
if (ret)
goto unlock;
/* create offline extents in potentially many transactions */
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) {
ret = scoutfs_data_init_offline_extent(inode, sm.i_size, lock);
if (ret)
goto unlock;
}
/* can only change size/dv on untouched regular files */
if ((sm.i_size != 0 || sm.data_version != 0) &&
((!S_ISREG(inode->i_mode) ||
@@ -602,12 +609,6 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
if (ret)
goto unlock;
if (sm.flags & SCOUTFS_IOC_SETATTR_MORE_OFFLINE) {
ret = scoutfs_data_init_offline_extent(inode, sm.i_size, lock);
if (ret)
goto release;
}
if (sm.data_version)
scoutfs_inode_set_data_version(inode, sm.data_version);
if (sm.i_size)
@@ -618,7 +619,6 @@ static long scoutfs_ioc_setattr_more(struct file *file, unsigned long arg)
scoutfs_update_inode_item(inode, lock, &ind_locks);
ret = 0;
release:
scoutfs_release_trans(sb);
unlock:
scoutfs_inode_index_unlock(sb, &ind_locks);
+1 -1
View File
@@ -157,7 +157,7 @@ static int lock_invalidate(struct super_block *sb, struct scoutfs_lock *lock,
mode != SCOUTFS_LOCK_NULL);
/* any transition from a mode allowed to dirty items has to write */
if (lock_mode_can_write(prev) && scoutfs_forest_has_dirty(sb)) {
if (lock_mode_can_write(prev) && scoutfs_trans_has_dirty(sb)) {
ret = scoutfs_trans_sync(sb, 1);
if (ret < 0)
return ret;
+65 -167
View File
@@ -34,12 +34,33 @@
#include "count.h"
#include "export.h"
#include "dir.h"
#include "extents.h"
#include "server.h"
#include "net.h"
#include "data.h"
struct lock_info;
#define STE_FMT "[%llu %llu %llu 0x%x]"
#define STE_ARGS(te) (te)->iblock, (te)->count, (te)->blkno, (te)->flags
#define STE_FIELDS(pref) \
__field(__u64, pref##_iblock) \
__field(__u64, pref##_count) \
__field(__u64, pref##_blkno) \
__field(__u8, pref##_flags)
#define STE_ASSIGN(pref, te) \
__entry->pref##_iblock = (te)->iblock; \
__entry->pref##_count = (te)->count; \
__entry->pref##_blkno = (te)->blkno; \
__entry->pref##_flags = (te)->flags;
#define STE_ENTRY_ARGS(pref) \
__entry->pref##_iblock, \
__entry->pref##_count, \
__entry->pref##_blkno, \
__entry->pref##_flags
#define DECLARE_TRACED_EXTENT(name) \
struct scoutfs_traced_extent name = {0}
TRACE_EVENT(scoutfs_setattr,
TP_PROTO(struct dentry *dentry, struct iattr *attr),
@@ -152,15 +173,17 @@ TRACE_EVENT(scoutfs_data_fiemap,
TRACE_EVENT(scoutfs_get_block,
TP_PROTO(struct super_block *sb, __u64 ino, __u64 iblock,
int create, int ret, __u64 blkno, size_t size),
int create, struct scoutfs_traced_extent *te,
int ret, __u64 blkno, size_t size),
TP_ARGS(sb, ino, iblock, create, ret, blkno, size),
TP_ARGS(sb, ino, iblock, create, te, ret, blkno, size),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(__u64, ino)
__field(__u64, iblock)
__field(int, create)
STE_FIELDS(ext)
__field(int, ret)
__field(__u64, blkno)
__field(size_t, size)
@@ -171,68 +194,58 @@ TRACE_EVENT(scoutfs_get_block,
__entry->ino = ino;
__entry->iblock = iblock;
__entry->create = create;
STE_ASSIGN(ext, te)
__entry->ret = ret;
__entry->blkno = blkno;
__entry->size = size;
),
TP_printk(SCSBF" ino %llu iblock %llu create %d ret %d bnr %llu "
"size %zu", SCSB_TRACE_ARGS, __entry->ino, __entry->iblock,
__entry->create, __entry->ret, __entry->blkno, __entry->size)
TP_printk(SCSBF" ino %llu iblock %llu create %d ext "STE_FMT" ret %d bnr %llu size %zu",
SCSB_TRACE_ARGS, __entry->ino, __entry->iblock,
__entry->create, STE_ENTRY_ARGS(ext), __entry->ret,
__entry->blkno, __entry->size)
);
TRACE_EVENT(scoutfs_data_alloc_block,
TP_PROTO(struct super_block *sb, struct inode *inode,
struct scoutfs_extent *ext, u64 iblock, u64 len,
u64 online_blocks, u64 offline_blocks),
TRACE_EVENT(scoutfs_data_file_extent_class,
TP_PROTO(struct super_block *sb, __u64 ino,
struct scoutfs_traced_extent *te),
TP_ARGS(sb, inode, ext, iblock, len, online_blocks, offline_blocks),
TP_ARGS(sb, ino, te),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
__field(__u64, ino)
se_trace_define(ext)
__field(__u64, iblock)
__field(__u64, len)
__field(__u64, online_blocks)
__field(__u64, offline_blocks)
STE_FIELDS(ext)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
__entry->ino = scoutfs_ino(inode);
se_trace_assign(ext, ext);
__entry->iblock = iblock;
__entry->len = len;
__entry->online_blocks = online_blocks;
__entry->offline_blocks = offline_blocks;
__entry->ino = ino;
STE_ASSIGN(ext, te)
),
TP_printk(SCSBF" ino %llu ext "SE_FMT" iblock %llu len %llu online_blocks %llu offline_blocks %llu",
SCSB_TRACE_ARGS, __entry->ino, se_trace_args(ext),
__entry->iblock, __entry->len, __entry->online_blocks,
__entry->offline_blocks)
TP_printk(SCSBF" ino %llu ext "STE_FMT,
SCSB_TRACE_ARGS, __entry->ino, STE_ENTRY_ARGS(ext))
);
TRACE_EVENT(scoutfs_data_alloc_block_ret,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext, int ret),
TP_ARGS(sb, ext, ret),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
se_trace_define(ext)
__field(int, ret)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
se_trace_assign(ext, ext);
__entry->ret = ret;
),
TP_printk(SCSBF" ext "SE_FMT" ret %d", SCSB_TRACE_ARGS,
se_trace_args(ext), __entry->ret)
DEFINE_EVENT(scoutfs_data_file_extent_class, scoutfs_data_alloc_block,
TP_PROTO(struct super_block *sb, __u64 ino,
struct scoutfs_traced_extent *te),
TP_ARGS(sb, ino, te)
);
DEFINE_EVENT(scoutfs_data_file_extent_class, scoutfs_data_prealloc_unwritten,
TP_PROTO(struct super_block *sb, __u64 ino,
struct scoutfs_traced_extent *te),
TP_ARGS(sb, ino, te)
);
DEFINE_EVENT(scoutfs_data_file_extent_class, scoutfs_data_extent_truncated,
TP_PROTO(struct super_block *sb, __u64 ino,
struct scoutfs_traced_extent *te),
TP_ARGS(sb, ino, te)
);
DEFINE_EVENT(scoutfs_data_file_extent_class, scoutfs_data_fiemap_extent,
TP_PROTO(struct super_block *sb, __u64 ino,
struct scoutfs_traced_extent *te),
TP_ARGS(sb, ino, te)
);
TRACE_EVENT(scoutfs_data_truncate_items,
@@ -260,10 +273,9 @@ TRACE_EVENT(scoutfs_data_truncate_items,
TRACE_EVENT(scoutfs_data_wait_check,
TP_PROTO(struct super_block *sb, __u64 ino, __u64 pos, __u64 len,
__u8 sef, __u8 op, __u64 ext_start, __u64 ext_len,
__u8 ext_flags, int ret),
__u8 sef, __u8 op, struct scoutfs_traced_extent *te, int ret),
TP_ARGS(sb, ino, pos, len, sef, op, ext_start, ext_len, ext_flags, ret),
TP_ARGS(sb, ino, pos, len, sef, op, te, ret),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
@@ -272,9 +284,7 @@ TRACE_EVENT(scoutfs_data_wait_check,
__field(__u64, len)
__field(__u8, sef)
__field(__u8, op)
__field(__u64, ext_start)
__field(__u64, ext_len)
__field(__u8, ext_flags)
STE_FIELDS(ext)
__field(int, ret)
),
@@ -285,16 +295,13 @@ TRACE_EVENT(scoutfs_data_wait_check,
__entry->len = len;
__entry->sef = sef;
__entry->op = op;
__entry->ext_start = ext_start;
__entry->ext_len = ext_len;
__entry->ext_flags = ext_flags;
STE_ASSIGN(ext, te)
__entry->ret = ret;
),
TP_printk(SCSBF" ino %llu pos %llu len %llu sef 0x%x op 0x%x ext_start %llu ext_len %llu ext_flags 0x%x ret %d",
TP_printk(SCSBF" ino %llu pos %llu len %llu sef 0x%x op 0x%x ext "STE_FMT" ret %d",
SCSB_TRACE_ARGS, __entry->ino, __entry->pos, __entry->len,
__entry->sef, __entry->op, __entry->ext_start,
__entry->ext_len, __entry->ext_flags, __entry->ret)
__entry->sef, __entry->op, STE_ENTRY_ARGS(ext), __entry->ret)
);
TRACE_EVENT(scoutfs_sync_fs,
@@ -1588,115 +1595,6 @@ TRACE_EVENT(scoutfs_btree_dirty_block,
__entry->bt_blkno, __entry->bt_seq)
);
DECLARE_EVENT_CLASS(scoutfs_extent_class,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext),
TP_STRUCT__entry(
SCSB_TRACE_FIELDS
se_trace_define(ext)
),
TP_fast_assign(
SCSB_TRACE_ASSIGN(sb);
se_trace_assign(ext, ext);
),
TP_printk(SCSBF" ext "SE_FMT,
SCSB_TRACE_ARGS, se_trace_args(ext))
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_insert,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_delete,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_next_input,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_next_output,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_prev_input,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_prev_output,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_add,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_extent_remove,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_truncate_next,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_truncate_remove,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_truncate_offline,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_get_server_extent,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_find_free_extent,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_alloc_block_next,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_get_block_next,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_get_block_intersection,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_fiemap_extent,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_data_return_server_extent,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_server_alloc_extent_next,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_server_alloc_extent_allocated,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_server_free_pending_extent,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
DEFINE_EVENT(scoutfs_extent_class, scoutfs_server_extent_io,
TP_PROTO(struct super_block *sb, struct scoutfs_extent *ext),
TP_ARGS(sb, ext)
);
TRACE_EVENT(scoutfs_online_offline_blocks,
TP_PROTO(struct inode *inode, s64 on_delta, s64 off_delta,
u64 on_now, u64 off_now),
+141 -393
View File
@@ -68,9 +68,7 @@ struct server_info {
/* server tracks seq use */
struct rw_semaphore seq_rwsem;
/* server tracks pending frees to be applied during commit */
struct rw_semaphore alloc_rwsem;
struct list_head pending_frees;
struct list_head clients;
unsigned long nr_clients;
@@ -103,291 +101,6 @@ struct commit_waiter {
int ret;
};
static void init_extent_btree_key(struct scoutfs_extent_btree_key *ebk,
u8 type, u64 major, u64 minor)
{
ebk->type = type;
ebk->major = cpu_to_be64(major);
ebk->minor = cpu_to_be64(minor);
}
static int init_extent_from_btree_key(struct scoutfs_extent *ext, u8 type,
struct scoutfs_extent_btree_key *ebk,
unsigned int key_bytes)
{
u64 start;
u64 len;
/* btree _next doesn't have last key limit */
if (ebk->type != type)
return -ENOENT;
if (key_bytes != sizeof(struct scoutfs_extent_btree_key) ||
(ebk->type != SCOUTFS_FREE_EXTENT_BLKNO_TYPE &&
ebk->type != SCOUTFS_FREE_EXTENT_BLOCKS_TYPE))
return -EIO; /* XXX corruption, bad key */
start = be64_to_cpu(ebk->major);
len = be64_to_cpu(ebk->minor);
if (ebk->type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE)
swap(start, len);
start -= len - 1;
return scoutfs_extent_init(ext, ebk->type, 0, start, len, 0, 0);
}
/*
* This is called by the extent core on behalf of the server who holds
* the appropriate locks to protect the many btree items that can be
* accessed on behalf of one extent operation.
*
* The free_blocks count in the super tracks the number of blocks in
* the primary extent index. We update it here instead of expecting
* callers to remember.
*/
static int server_extent_io(struct super_block *sb, int op,
struct scoutfs_extent *ext, void *data)
{
DECLARE_SERVER_INFO(sb, server);
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
struct scoutfs_extent_btree_key ebk;
SCOUTFS_BTREE_ITEM_REF(iref);
bool mirror = false;
u8 mirror_type;
u8 mirror_op = 0;
int ret;
int err;
trace_scoutfs_server_extent_io(sb, ext);
if (WARN_ON_ONCE(ext->type != SCOUTFS_FREE_EXTENT_BLKNO_TYPE &&
ext->type != SCOUTFS_FREE_EXTENT_BLOCKS_TYPE))
return -EINVAL;
if (ext->type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE &&
(op == SEI_INSERT || op == SEI_DELETE)) {
mirror = true;
mirror_type = SCOUTFS_FREE_EXTENT_BLOCKS_TYPE;
mirror_op = op == SEI_INSERT ? SEI_DELETE : SEI_INSERT;
}
init_extent_btree_key(&ebk, ext->type, ext->start + ext->len - 1,
ext->len);
if (ext->type == SCOUTFS_FREE_EXTENT_BLOCKS_TYPE)
swap(ebk.major, ebk.minor);
if (op == SEI_NEXT || op == SEI_PREV) {
if (op == SEI_NEXT)
ret = scoutfs_btree_next(sb, &super->alloc_root,
&ebk, sizeof(ebk), &iref);
else
ret = scoutfs_btree_prev(sb, &super->alloc_root,
&ebk, sizeof(ebk), &iref);
if (ret == 0) {
ret = init_extent_from_btree_key(ext, ext->type,
iref.key,
iref.key_len);
scoutfs_btree_put_iref(&iref);
}
} else if (op == SEI_INSERT) {
ret = scoutfs_btree_insert(sb, &server->alloc, &server->wri,
&super->alloc_root,
&ebk, sizeof(ebk), NULL, 0);
} else if (op == SEI_DELETE) {
ret = scoutfs_btree_delete(sb, &server->alloc, &server->wri,
&super->alloc_root,
&ebk, sizeof(ebk));
} else {
ret = WARN_ON_ONCE(-EINVAL);
}
if (ret == 0 && mirror) {
swap(ext->type, mirror_type);
ret = server_extent_io(sb, op, ext, data);
swap(ext->type, mirror_type);
if (ret < 0) {
err = server_extent_io(sb, mirror_op, ext, data);
if (err)
scoutfs_corruption(sb,
SC_SERVER_EXTENT_CLEANUP,
corrupt_server_extent_cleanup,
"op %u ext "SE_FMT" ret %d",
op, SE_ARG(ext), err);
}
}
if (ret == 0 && ext->type == SCOUTFS_FREE_EXTENT_BLKNO_TYPE) {
if (op == SEI_INSERT)
le64_add_cpu(&super->free_blocks, ext->len);
else if (op == SEI_DELETE)
le64_add_cpu(&super->free_blocks, -ext->len);
}
return ret;
}
/*
* Allocate an extent of the given length in the first smallest free
* extent that contains it.
*/
static int alloc_extent(struct super_block *sb, u64 blocks,
u64 *start, u64 *len)
{
struct server_info *server = SCOUTFS_SB(sb)->server_info;
struct scoutfs_extent ext;
int ret;
*start = 0;
*len = 0;
down_write(&server->alloc_rwsem);
scoutfs_extent_init(&ext, SCOUTFS_FREE_EXTENT_BLOCKS_TYPE, 0,
0, blocks, 0, 0);
ret = scoutfs_extent_next(sb, server_extent_io, &ext, NULL);
if (ret == -ENOENT)
ret = scoutfs_extent_prev(sb, server_extent_io, &ext, NULL);
if (ret) {
if (ret == -ENOENT)
ret = -ENOSPC;
goto out;
}
trace_scoutfs_server_alloc_extent_next(sb, &ext);
ext.type = SCOUTFS_FREE_EXTENT_BLKNO_TYPE;
ext.len = min(blocks, ext.len);
ret = scoutfs_extent_remove(sb, server_extent_io, &ext, NULL);
if (ret)
goto out;
trace_scoutfs_server_alloc_extent_allocated(sb, &ext);
*start = ext.start;
*len = ext.len;
ret = 0;
out:
up_write(&server->alloc_rwsem);
if (ret)
scoutfs_inc_counter(sb, server_extent_alloc_error);
else
scoutfs_inc_counter(sb, server_extent_alloc);
return ret;
}
struct pending_free_extent {
struct list_head head;
u64 start;
u64 len;
};
/*
* Now that the transaction's done we can apply all the pending frees.
* The list entries are totally unsorted so this is the first time that
* we can discover corruption from duplicated frees, etc. This can also
* fail on normal transient io or memory errors.
*
* We can't unwind if this fails. The caller can freak out or keep
* trying forever.
*/
static int apply_pending_frees(struct super_block *sb)
{
struct server_info *server = SCOUTFS_SB(sb)->server_info;
struct pending_free_extent *pfe;
struct pending_free_extent *tmp;
struct scoutfs_extent ext;
int ret;
down_write(&server->alloc_rwsem);
list_for_each_entry_safe(pfe, tmp, &server->pending_frees, head) {
scoutfs_inc_counter(sb, server_free_pending_extent);
scoutfs_extent_init(&ext, SCOUTFS_FREE_EXTENT_BLKNO_TYPE, 0,
pfe->start, pfe->len, 0, 0);
trace_scoutfs_server_free_pending_extent(sb, &ext);
ret = scoutfs_extent_add(sb, server_extent_io, &ext, NULL);
if (ret) {
scoutfs_inc_counter(sb, server_free_pending_error);
break;
}
list_del_init(&pfe->head);
kfree(pfe);
}
up_write(&server->alloc_rwsem);
return 0;
}
/*
* If there are still pending frees to destroy it means the server didn't
* shut down cleanly and that's not well supported today so we want to
* have it holler if this happens. In the future we'd cleanly support
* forced shutdown that had been told that it's OK to throw away dirty
* state.
*/
static int destroy_pending_frees(struct super_block *sb)
{
struct server_info *server = SCOUTFS_SB(sb)->server_info;
struct pending_free_extent *pfe;
struct pending_free_extent *tmp;
WARN_ON_ONCE(!list_empty(&server->pending_frees));
down_write(&server->alloc_rwsem);
list_for_each_entry_safe(pfe, tmp, &server->pending_frees, head) {
list_del_init(&pfe->head);
kfree(pfe);
}
up_write(&server->alloc_rwsem);
return 0;
}
/*
* We can't satisfy allocations with freed extents until the removed
* references to the freed extents have been committed. We add freed
* extents to a list that is only applied to the persistent indexes as
* the transaction is being committed and the current transaction won't
* try to allocate any more extents. If we didn't do this then we could
* write to referenced data as part of the commit that frees it. If the
* commit was interrupted the stable data could have been overwritten.
*/
static int free_extent(struct super_block *sb, u64 start, u64 len)
{
struct server_info *server = SCOUTFS_SB(sb)->server_info;
struct pending_free_extent *pfe;
int ret;
scoutfs_inc_counter(sb, server_free_extent);
down_write(&server->alloc_rwsem);
pfe = kmalloc(sizeof(struct pending_free_extent), GFP_NOFS);
if (!pfe) {
ret = -ENOMEM;
} else {
pfe->start = start;
pfe->len = len;
list_add_tail(&pfe->head, &server->pending_frees);
ret = 0;
}
up_write(&server->alloc_rwsem);
return ret;
}
static void stop_server(struct server_info *server)
{
/* wait_event/wake_up provide barriers */
@@ -444,23 +157,60 @@ static int add_uninit_balloc_items(struct super_block *sb,
struct server_info *server,
struct scoutfs_super_block *super)
{
u64 next = le64_to_cpu(super->next_uninit_free_block);
u64 total = le64_to_cpu(super->total_blocks);
u64 next = le64_to_cpu(super->next_uninit_meta_blkno);
u64 last = le64_to_cpu(super->last_uninit_meta_blkno);
u64 nr;
int ret;
if (next > last)
return 0;
/* next_uninit should always start a new item */
if (WARN_ON_ONCE(next & SCOUTFS_BALLOC_ITEM_BIT_MASK))
return -EIO;
nr = min_t(u64, total - next,
nr = min_t(u64, last - next + 1,
round_up(512 * 1024 * 1024 / SCOUTFS_BLOCK_SIZE,
SCOUTFS_BALLOC_ITEM_BITS));
ret = scoutfs_balloc_add_alloc_bulk(sb, &server->alloc, &server->wri,
next, nr);
if (ret == 0)
le64_add_cpu(&super->next_uninit_free_block, nr);
le64_add_cpu(&super->next_uninit_meta_blkno, nr);
return ret;
}
/*
* Add newly initialized free block bitmap items.
*/
static int add_uninit_data_alloc_items(struct super_block *sb,
struct server_info *server,
struct scoutfs_super_block *super)
{
int nr = 16;
u64 next;
u64 last;
int ret;
while (nr-- > 0) {
next = le64_to_cpu(super->next_uninit_data_blkno);
last = le64_to_cpu(super->last_uninit_data_blkno);
if (next > last) {
ret = 0;
break;
}
ret = scoutfs_data_add_free_blocks(sb, &server->alloc,
&server->wri,
&super->core_data_alloc,
next, last - next + 1);
if (ret <= 0)
break;
le64_add_cpu(&super->next_uninit_data_blkno, ret);
ret = 0;
}
return ret;
}
@@ -499,17 +249,14 @@ static void scoutfs_server_commit_func(struct work_struct *work)
down_write(&server->commit_rwsem);
/* try to free first which can dirty the btrees */
ret = apply_pending_frees(sb);
if (ret) {
scoutfs_err(sb, "server error freeing extents: %d", ret);
goto out;
}
/* XXX not sure what to do about failure here */
ret = add_uninit_balloc_items(sb, server, super);
BUG_ON(ret);
/* XXX not sure what to do about failure here */
ret = add_uninit_data_alloc_items(sb, server, super);
BUG_ON(ret);
ret = scoutfs_block_writer_write(sb, &server->wri);
if (ret) {
scoutfs_err(sb, "server error writing btree blocks: %d", ret);
@@ -579,94 +326,6 @@ out:
return scoutfs_net_response(sb, conn, cmd, id, ret, &ial, sizeof(ial));
}
/*
* Give the client an extent allocation of len blocks. We leave the
* details to the extent allocator.
*/
static int server_alloc_extent(struct super_block *sb,
struct scoutfs_net_connection *conn,
u8 cmd, u64 id, void *arg, u16 arg_len)
{
DECLARE_SERVER_INFO(sb, server);
struct commit_waiter cw;
struct scoutfs_net_extent nex = {0,};
__le64 leblocks;
u64 start;
u64 len;
int ret;
if (arg_len != sizeof(leblocks)) {
ret = -EINVAL;
goto out;
}
memcpy(&leblocks, arg, arg_len);
down_read(&server->commit_rwsem);
ret = alloc_extent(sb, le64_to_cpu(leblocks), &start, &len);
if (ret == 0)
queue_commit_work(server, &cw);
up_read(&server->commit_rwsem);
if (ret == 0)
ret = wait_for_commit(&cw);
if (ret)
goto out;
nex.start = cpu_to_le64(start);
nex.len = cpu_to_le64(len);
out:
return scoutfs_net_response(sb, conn, cmd, id, ret, &nex, sizeof(nex));
}
static bool invalid_net_extent_list(struct scoutfs_net_extent_list *nexl,
unsigned data_len)
{
return (data_len < sizeof(struct scoutfs_net_extent_list)) ||
(le64_to_cpu(nexl->nr) > SCOUTFS_NET_EXTENT_LIST_MAX_NR) ||
(data_len != offsetof(struct scoutfs_net_extent_list,
extents[le64_to_cpu(nexl->nr)]));
}
static int server_free_extents(struct super_block *sb,
struct scoutfs_net_connection *conn,
u8 cmd, u64 id, void *arg, u16 arg_len)
{
DECLARE_SERVER_INFO(sb, server);
struct scoutfs_net_extent_list *nexl;
struct commit_waiter cw;
int ret = 0;
int err;
u64 i;
nexl = arg;
if (invalid_net_extent_list(nexl, arg_len)) {
ret = -EINVAL;
goto out;
}
down_read(&server->commit_rwsem);
for (i = 0; i < le64_to_cpu(nexl->nr); i++) {
ret = free_extent(sb, le64_to_cpu(nexl->extents[i].start),
le64_to_cpu(nexl->extents[i].len));
if (ret)
break;
}
if (i > 0)
queue_commit_work(server, &cw);
up_read(&server->commit_rwsem);
if (i > 0) {
err = wait_for_commit(&cw);
if (ret == 0)
ret = err;
}
out:
return scoutfs_net_response(sb, conn, cmd, id, ret, NULL, 0);
}
/*
* Give the client references to stable persistent trees that they'll
* use to write their next transaction.
@@ -728,9 +387,8 @@ static int server_get_log_trees(struct super_block *sb,
memset(&ltv, 0, sizeof(ltv));
}
/* ensure client has enough free metadata blocks for a transaction */
target = (64*1024*1024) / SCOUTFS_BLOCK_SIZE;
/* XXX arbitrarily give client enough metadata for a transaction */
while (le64_to_cpu(ltv.alloc_root.total_free) < target) {
from = le64_to_cpu(super->core_balloc_cursor);
at_least = target - le64_to_cpu(ltv.alloc_root.total_free);
@@ -747,9 +405,20 @@ static int server_get_log_trees(struct super_block *sb,
goto unlock;
super->core_balloc_cursor = cpu_to_le64(next_past);
}
/* fill client's data block allocator */
target = (2ULL*1024*1024*1024) / SCOUTFS_BLOCK_SIZE;
down_write(&server->alloc_rwsem);
ret = scoutfs_data_move_alloc_bits(sb, &server->alloc, &server->wri,
&ltv.data_alloc,
&super->core_data_alloc,
&super->core_data_alloc_cursor,
target);
up_write(&server->alloc_rwsem);
if (ret < 0)
goto unlock;
/* update client's log tree's item */
ret = scoutfs_btree_force(sb, &server->alloc, &server->wri,
&super->logs_root, &ltk, sizeof(ltk),
@@ -768,6 +437,8 @@ unlock:
lt.free_root = ltv.free_root;
lt.item_root = ltv.item_root;
lt.bloom_ref = ltv.bloom_ref;
lt.data_alloc = ltv.data_alloc;
lt.data_free = ltv.data_free;
lt.rid = be64_to_le64(ltk.rid);
lt.nr = be64_to_le64(ltk.nr);
}
@@ -824,10 +495,14 @@ static int server_commit_log_trees(struct super_block *sb,
goto unlock;
}
/* XXX probably want to merge free blocks */
ltv.alloc_root = lt->alloc_root;
ltv.free_root = lt->free_root;
ltv.item_root = lt->item_root;
ltv.bloom_ref = lt->bloom_ref;
ltv.data_alloc = lt->data_alloc;
ltv.data_free = lt->data_free;
ret = scoutfs_btree_update(sb, &server->alloc, &server->wri,
&super->logs_root, &ltk, sizeof(ltk),
@@ -846,6 +521,82 @@ out:
return scoutfs_net_response(sb, conn, cmd, id, ret, NULL, 0);
}
/*
* A client is being evicted so we want to reclaim resources from their
* log tree items. The item trees and bloom refs stay around to be read
* and eventually merged and we reclaim all the allocator items.
*
* The caller holds the commit rwsem which means we do all this work
* in one server commit. We'll need to keep the total amount of blocks
* in trees in check.
*
* By the time we're evicting a client they've either synced their data
* or have been forcefully removed. The free blocks in the allocator
* roots are stable and can be merged back into allocator items for use
* without risking overwriting stable data.
*/
static int reclaim_log_trees(struct super_block *sb, u64 rid)
{
struct scoutfs_super_block *super = &SCOUTFS_SB(sb)->super;
DECLARE_SERVER_INFO(sb, server);
SCOUTFS_BTREE_ITEM_REF(iref);
struct scoutfs_log_trees_key ltk;
struct scoutfs_log_trees_val ltv;
__le64 curs;
u64 tot;
int ret;
memset(&ltk, 0, sizeof(ltk));
memset(&ltv, 0, sizeof(ltv));
mutex_lock(&server->logs_mutex);
down_write(&server->alloc_rwsem);
/* find the client's existing item */
ltk.rid = cpu_to_be64(rid);
ltk.nr = 0;
ret = scoutfs_btree_next(sb, &super->logs_root,
&ltk, sizeof(ltk), &iref);
if (ret == 0) {
if (iref.key_len == sizeof(struct scoutfs_log_trees_key) &&
iref.val_len == sizeof(struct scoutfs_log_trees_val)) {
memcpy(&ltk, iref.key, iref.key_len);
memcpy(&ltv, iref.val, iref.val_len);
if (be64_to_cpu(ltk.rid) != rid)
ret = -ENOENT;
} else {
ret = -EIO;
}
scoutfs_btree_put_iref(&iref);
}
if (ret < 0) {
if (ret == -ENOENT)
ret = 0;
goto out;
}
tot = le64_to_cpu(super->core_data_alloc.total_free) +
le64_to_cpu(ltv.data_alloc.total_free);
curs = 0;
ret = scoutfs_data_move_alloc_bits(sb, &server->alloc, &server->wri,
&super->core_data_alloc,
&ltv.data_alloc, &curs, tot);
if (ret < 0)
goto out;
tot = le64_to_cpu(super->core_data_alloc.total_free) +
le64_to_cpu(ltv.data_free.total_free);
curs = 0;
ret = scoutfs_data_move_alloc_bits(sb, &server->alloc, &server->wri,
&super->core_data_alloc,
&ltv.data_free, &curs, tot);
out:
up_write(&server->alloc_rwsem);
mutex_unlock(&server->logs_mutex);
return ret;
}
/*
* Give the client the next sequence number for their transaction. They
* provide their previous transaction sequence number that they've
@@ -1455,6 +1206,7 @@ static void farewell_worker(struct work_struct *work)
ret = scoutfs_lock_server_farewell(sb, fw->rid) ?:
remove_trans_seq(sb, fw->rid) ?:
reclaim_log_trees(sb, fw->rid) ?:
delete_mounted_client(sb, fw->rid);
if (ret == 0)
queue_commit_work(server, &cw);
@@ -1564,8 +1316,6 @@ static int server_farewell(struct super_block *sb,
static scoutfs_net_request_t server_req_funcs[] = {
[SCOUTFS_NET_CMD_GREETING] = server_greeting,
[SCOUTFS_NET_CMD_ALLOC_INODES] = server_alloc_inodes,
[SCOUTFS_NET_CMD_ALLOC_EXTENT] = server_alloc_extent,
[SCOUTFS_NET_CMD_FREE_EXTENTS] = server_free_extents,
[SCOUTFS_NET_CMD_GET_LOG_TREES] = server_get_log_trees,
[SCOUTFS_NET_CMD_COMMIT_LOG_TREES] = server_commit_log_trees,
[SCOUTFS_NET_CMD_ADVANCE_SEQ] = server_advance_seq,
@@ -1697,7 +1447,6 @@ shutdown:
flush_work(&server->commit_work);
server->conn = NULL;
destroy_pending_frees(sb);
scoutfs_lock_server_destroy(sb);
out:
@@ -1793,7 +1542,6 @@ int scoutfs_server_setup(struct super_block *sb)
INIT_WORK(&server->commit_work, scoutfs_server_commit_func);
init_rwsem(&server->seq_rwsem);
init_rwsem(&server->alloc_rwsem);
INIT_LIST_HEAD(&server->pending_frees);
INIT_LIST_HEAD(&server->clients);
mutex_init(&server->farewell_mutex);
INIT_LIST_HEAD(&server->farewell_requests);
+1 -1
View File
@@ -441,7 +441,7 @@ static int scoutfs_fill_super(struct super_block *sb, void *data, int silent)
scoutfs_client_setup(sb) ?:
scoutfs_lock_rid(sb, SCOUTFS_LOCK_WRITE, 0, sbi->rid,
&sbi->rid_lock) ?:
scoutfs_forest_get_log_trees(sb);
scoutfs_trans_get_log_trees(sb);
if (ret)
goto out;
+57 -6
View File
@@ -25,6 +25,8 @@
#include "counters.h"
#include "client.h"
#include "inode.h"
#include "balloc.h"
#include "block.h"
#include "scoutfs_trace.h"
/*
@@ -60,6 +62,10 @@ struct trans_info {
unsigned reserved_vals;
unsigned holders;
bool writing;
struct scoutfs_log_trees lt;
struct scoutfs_balloc_allocator alloc;
struct scoutfs_block_writer wri;
};
#define DECLARE_TRANS_INFO(sb, name) \
@@ -77,6 +83,48 @@ static bool drained_holders(struct trans_info *tri)
return drained;
}
static int commit_btrees(struct super_block *sb)
{
DECLARE_TRANS_INFO(sb, tri);
struct scoutfs_log_trees lt;
lt = tri->lt;
lt.alloc_root = tri->alloc.alloc_root;
lt.free_root = tri->alloc.free_root;
scoutfs_forest_get_btrees(sb, &lt);
scoutfs_data_get_btrees(sb, &lt);
return scoutfs_client_commit_log_trees(sb, &lt);
}
/*
* This gets all the resources from the server that the client will
* need during the transaction.
*/
int scoutfs_trans_get_log_trees(struct super_block *sb)
{
DECLARE_TRANS_INFO(sb, tri);
struct scoutfs_log_trees lt;
int ret = 0;
ret = scoutfs_client_get_log_trees(sb, &lt);
if (ret == 0) {
tri->lt = lt;
scoutfs_balloc_init(&tri->alloc, &lt.alloc_root, &lt.free_root);
scoutfs_block_writer_init(sb, &tri->wri);
scoutfs_forest_init_btrees(sb, &tri->alloc, &tri->wri, &lt);
scoutfs_data_init_btrees(sb, &tri->alloc, &tri->wri, &lt);
}
return ret;
}
bool scoutfs_trans_has_dirty(struct super_block *sb)
{
DECLARE_TRANS_INFO(sb, tri);
return scoutfs_block_writer_has_dirty(sb, &tri->wri);
}
/*
* This work func is responsible for writing out all the dirty blocks
* that make up the current dirty transaction. It prevents writers from
@@ -113,18 +161,19 @@ void scoutfs_trans_write_func(struct work_struct *work)
wait_event(sbi->trans_hold_wq, drained_holders(tri));
trace_scoutfs_trans_write_func(sb, scoutfs_forest_dirty_bytes(sb));
trace_scoutfs_trans_write_func(sb,
scoutfs_block_writer_dirty_bytes(sb, &tri->wri));
if (scoutfs_forest_has_dirty(sb)) {
if (scoutfs_block_writer_has_dirty(sb, &tri->wri)) {
if (sbi->trans_deadline_expired)
scoutfs_inc_counter(sb, trans_commit_timer);
ret = scoutfs_inode_walk_writeback(sb, true) ?:
scoutfs_forest_write(sb) ?:
scoutfs_block_writer_write(sb, &tri->wri) ?:
scoutfs_inode_walk_writeback(sb, false) ?:
scoutfs_forest_commit(sb) ?:
commit_btrees(sb) ?:
scoutfs_client_advance_seq(sb, &sbi->trans_seq) ?:
scoutfs_forest_get_log_trees(sb);
scoutfs_trans_get_log_trees(sb);
if (ret)
goto out;
@@ -297,7 +346,8 @@ static bool acquired_hold(struct super_block *sb,
vals = tri->reserved_vals + cnt->vals;
/* XXX arbitrarily limit to 8 meg transactions */
if (scoutfs_forest_dirty_bytes(sb) >= (8 * 1024 * 1024)) {
if (scoutfs_block_writer_dirty_bytes(sb, &tri->wri) >=
(8 * 1024 * 1024)) {
scoutfs_inc_counter(sb, trans_commit_full);
queue_trans_work(sbi);
goto out;
@@ -481,6 +531,7 @@ void scoutfs_shutdown_trans(struct super_block *sb)
DECLARE_TRANS_INFO(sb, tri);
if (tri) {
scoutfs_block_writer_forget_all(sb, &tri->wri);
if (sbi->trans_write_workq) {
cancel_delayed_work_sync(&sbi->trans_write_work);
destroy_workqueue(sbi->trans_write_workq);
+3
View File
@@ -16,6 +16,9 @@ void scoutfs_release_trans(struct super_block *sb);
void scoutfs_trans_track_item(struct super_block *sb, signed items,
signed vals);
int scoutfs_trans_get_log_trees(struct super_block *sb);
bool scoutfs_trans_has_dirty(struct super_block *sb);
int scoutfs_setup_trans(struct super_block *sb);
void scoutfs_shutdown_trans(struct super_block *sb);