scoutfs: lock manifest root request

The manifest root request processing samples the stable_manifest_root in
the server info.  The stable_manifest_root is updated after a
commit has suceeded.

The read of stable_manifest_root in request processing was locking the
manifest.  The update during commit doesn't lock the manifest so these
paths were racing.  The race is very tight, a few cpu stores, but it
could in theory give a client a malformed root that could be
misinterpreted as corruption.

Add a seqcount around the store of the stable manifest root during
commit and its load during request processing.  This ensures that
clients always get a consistent manifest root.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2018-04-27 09:06:35 -07:00
committed by Zach Brown
parent 7d7f8e45b7
commit 24cc5cc296
+10 -5
View File
@@ -61,6 +61,7 @@ struct server_info {
wait_queue_head_t compaction_waitq;
/* server remembers the stable manifest root for clients */
seqcount_t stable_seqcount;
struct scoutfs_btree_root stable_manifest_root;
/* server tracks seq use */
@@ -166,8 +167,11 @@ static void scoutfs_server_commit_func(struct work_struct *work)
scoutfs_btree_write_complete(sb);
write_seqcount_begin(&server->stable_seqcount);
server->stable_manifest_root =
SCOUTFS_SB(sb)->super.manifest.root;
write_seqcount_end(&server->stable_seqcount);
scoutfs_advance_dirty_super(sb);
} else {
ret = 0;
@@ -530,15 +534,15 @@ static int process_get_manifest_root(struct server_connection *conn, u64 id,
u8 type, void *data, unsigned data_len)
{
struct server_info *server = conn->server;
struct super_block *sb = server->sb;
struct scoutfs_btree_root root;
unsigned int start;
int ret;
if (data_len == 0) {
scoutfs_manifest_lock(sb);
memcpy(&root, &server->stable_manifest_root,
sizeof(struct scoutfs_btree_root));
scoutfs_manifest_unlock(sb);
do {
start = read_seqcount_begin(&server->stable_seqcount);
root = server->stable_manifest_root;
} while (read_seqcount_retry(&server->stable_seqcount, start));
ret = 0;
} else {
ret = -EINVAL;
@@ -1062,6 +1066,7 @@ int scoutfs_server_setup(struct super_block *sb)
init_llist_head(&server->commit_waiters);
INIT_WORK(&server->commit_work, scoutfs_server_commit_func);
init_waitqueue_head(&server->compaction_waitq);
seqcount_init(&server->stable_seqcount);
spin_lock_init(&server->seq_lock);
INIT_LIST_HEAD(&server->pending_seqs);