mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-10 21:50:20 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8596c9ad45 | ||
|
|
8a705ea380 | ||
|
|
4784ccdfd5 | ||
|
|
778c2769df | ||
|
|
9e3529060e | ||
|
|
1672b3ecec | ||
|
|
55f9435fad | ||
|
|
072f6868d3 | ||
|
|
8a64b46a2f | ||
|
|
14901c39aa |
@@ -1,6 +1,28 @@
|
||||
Versity ScoutFS Release Notes
|
||||
=============================
|
||||
|
||||
---
|
||||
v1.16
|
||||
\
|
||||
*Oct 4, 2023*
|
||||
|
||||
Fix an issue where the server could hang on startup if its persistent
|
||||
allocator structures were left in a specific degraded state by the
|
||||
previously active server.
|
||||
|
||||
---
|
||||
v1.15
|
||||
\
|
||||
*Jul 17, 2023*
|
||||
|
||||
Process log btree merge splicing in multiple commits. This prevents a
|
||||
rare case where pending log merge completions contain more work than can
|
||||
be done in a single server commit, causing the server to trigger an
|
||||
assert shortly after starting.
|
||||
|
||||
Fix spurious EINVAL from data writes when data\_prealloc\_contig\_only was
|
||||
set to 0.
|
||||
|
||||
---
|
||||
v1.14
|
||||
\
|
||||
|
||||
@@ -458,11 +458,9 @@ static int alloc_block(struct super_block *sb, struct inode *inode,
|
||||
/*
|
||||
* Preallocation within aligned regions tries to
|
||||
* allocate an extent to fill the hole in the region
|
||||
* that contains iblock. We search for a next extent
|
||||
* from the start of the region. If it's at the start
|
||||
* we might have to search again to find an existing
|
||||
* extent at the end of the region. (This next could be
|
||||
* given to us by the caller).
|
||||
* that contains iblock. We'd have to add a bit of plumbing
|
||||
* to find previous extents so we only search for a next
|
||||
* extent from the front of the region and from iblock.
|
||||
*/
|
||||
div64_u64_rem(iblock, opts.data_prealloc_blocks, &rem);
|
||||
start = iblock - rem;
|
||||
@@ -473,15 +471,15 @@ static int alloc_block(struct super_block *sb, struct inode *inode,
|
||||
|
||||
/* trim count if there's an extent in the region before iblock */
|
||||
if (found.len && found.start < iblock) {
|
||||
count -= (found.start + found.len) - start;
|
||||
start = found.start + found.len;
|
||||
count -= iblock - start;
|
||||
start = iblock;
|
||||
/* see if there's also an extent after iblock */
|
||||
ret = scoutfs_ext_next(sb, &data_ext_ops, &args, iblock, 1, &found);
|
||||
if (ret < 0 && ret != -ENOENT)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* trim count by a next extent in the region */
|
||||
/* trim count by next extent after iblock */
|
||||
if (found.len && found.start > start && found.start < start + count)
|
||||
count = (found.start - start);
|
||||
}
|
||||
|
||||
@@ -1896,8 +1896,9 @@ DEFINE_EVENT(scoutfs_server_client_count_class, scoutfs_server_client_down,
|
||||
|
||||
DECLARE_EVENT_CLASS(scoutfs_server_commit_users_class,
|
||||
TP_PROTO(struct super_block *sb, int holding, int applying, int nr_holders,
|
||||
u32 avail_before, u32 freed_before, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, exceeded),
|
||||
u32 avail_before, u32 freed_before, int committing, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, committing,
|
||||
exceeded),
|
||||
TP_STRUCT__entry(
|
||||
SCSB_TRACE_FIELDS
|
||||
__field(int, holding)
|
||||
@@ -1905,6 +1906,7 @@ DECLARE_EVENT_CLASS(scoutfs_server_commit_users_class,
|
||||
__field(int, nr_holders)
|
||||
__field(__u32, avail_before)
|
||||
__field(__u32, freed_before)
|
||||
__field(int, committing)
|
||||
__field(int, exceeded)
|
||||
),
|
||||
TP_fast_assign(
|
||||
@@ -1914,31 +1916,33 @@ DECLARE_EVENT_CLASS(scoutfs_server_commit_users_class,
|
||||
__entry->nr_holders = nr_holders;
|
||||
__entry->avail_before = avail_before;
|
||||
__entry->freed_before = freed_before;
|
||||
__entry->committing = !!committing;
|
||||
__entry->exceeded = !!exceeded;
|
||||
),
|
||||
TP_printk(SCSBF" holding %u applying %u nr %u avail_before %u freed_before %u exceeded %u",
|
||||
TP_printk(SCSBF" holding %u applying %u nr %u avail_before %u freed_before %u committing %u exceeded %u",
|
||||
SCSB_TRACE_ARGS, __entry->holding, __entry->applying, __entry->nr_holders,
|
||||
__entry->avail_before, __entry->freed_before, __entry->exceeded)
|
||||
__entry->avail_before, __entry->freed_before, __entry->committing,
|
||||
__entry->exceeded)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_server_commit_users_class, scoutfs_server_commit_hold,
|
||||
TP_PROTO(struct super_block *sb, int holding, int applying, int nr_holders,
|
||||
u32 avail_before, u32 freed_before, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, exceeded)
|
||||
u32 avail_before, u32 freed_before, int committing, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, committing, exceeded)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_server_commit_users_class, scoutfs_server_commit_apply,
|
||||
TP_PROTO(struct super_block *sb, int holding, int applying, int nr_holders,
|
||||
u32 avail_before, u32 freed_before, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, exceeded)
|
||||
u32 avail_before, u32 freed_before, int committing, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, committing, exceeded)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_server_commit_users_class, scoutfs_server_commit_start,
|
||||
TP_PROTO(struct super_block *sb, int holding, int applying, int nr_holders,
|
||||
u32 avail_before, u32 freed_before, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, exceeded)
|
||||
u32 avail_before, u32 freed_before, int committing, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, committing, exceeded)
|
||||
);
|
||||
DEFINE_EVENT(scoutfs_server_commit_users_class, scoutfs_server_commit_end,
|
||||
TP_PROTO(struct super_block *sb, int holding, int applying, int nr_holders,
|
||||
u32 avail_before, u32 freed_before, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, exceeded)
|
||||
u32 avail_before, u32 freed_before, int committing, int exceeded),
|
||||
TP_ARGS(sb, holding, applying, nr_holders, avail_before, freed_before, committing, exceeded)
|
||||
);
|
||||
|
||||
#define slt_symbolic(mode) \
|
||||
|
||||
@@ -67,6 +67,7 @@ struct commit_users {
|
||||
unsigned int nr_holders;
|
||||
u32 avail_before;
|
||||
u32 freed_before;
|
||||
bool committing;
|
||||
bool exceeded;
|
||||
};
|
||||
|
||||
@@ -84,7 +85,7 @@ do { \
|
||||
__typeof__(cusers) _cusers = (cusers); \
|
||||
trace_scoutfs_server_commit_##which(sb, !list_empty(&_cusers->holding), \
|
||||
!list_empty(&_cusers->applying), _cusers->nr_holders, _cusers->avail_before, \
|
||||
_cusers->freed_before, _cusers->exceeded); \
|
||||
_cusers->freed_before, _cusers->committing, _cusers->exceeded); \
|
||||
} while (0)
|
||||
|
||||
struct server_info {
|
||||
@@ -282,6 +283,14 @@ struct commit_hold {
|
||||
* per-holder allocation consumption tracking. The best we can do is
|
||||
* flag all the current holders so that as they release we can see
|
||||
* everyone involved in crossing the limit.
|
||||
*
|
||||
* The consumption of space to record freed blocks is tricky. The
|
||||
* freed_before value was the space available as the holder started.
|
||||
* But that happens before we actually dirty the first block in the
|
||||
* freed list. If that block is too full then we just allocate a new
|
||||
* empty first block. In that case the current remaining here can be a
|
||||
* lot more than the initial freed_before. We account for that and
|
||||
* treat freed_before as the maximum capacity.
|
||||
*/
|
||||
static void check_holder_budget(struct super_block *sb, struct server_info *server,
|
||||
struct commit_users *cusers)
|
||||
@@ -301,8 +310,13 @@ static void check_holder_budget(struct super_block *sb, struct server_info *serv
|
||||
return;
|
||||
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &avail_now, &freed_now);
|
||||
|
||||
avail_used = cusers->avail_before - avail_now;
|
||||
freed_used = cusers->freed_before - freed_now;
|
||||
if (freed_now < cusers->freed_before)
|
||||
freed_used = cusers->freed_before - freed_now;
|
||||
else
|
||||
freed_used = SCOUTFS_ALLOC_LIST_MAX_BLOCKS - freed_now;
|
||||
|
||||
budget = cusers->nr_holders * COMMIT_HOLD_ALLOC_BUDGET;
|
||||
if (avail_used <= budget && freed_used <= budget)
|
||||
return;
|
||||
@@ -325,31 +339,18 @@ static void check_holder_budget(struct super_block *sb, struct server_info *serv
|
||||
/*
|
||||
* We don't have per-holder consumption. We allow commit holders as
|
||||
* long as the total budget of all the holders doesn't exceed the alloc
|
||||
* resources that were available
|
||||
* resources that were available. If a hold is waiting for budget
|
||||
* availability in the allocators then we try and kick off a commit to
|
||||
* fill and use the next allocators after the current transaction.
|
||||
*/
|
||||
static bool commit_alloc_has_room(struct server_info *server, struct commit_users *cusers,
|
||||
unsigned int more_holders)
|
||||
{
|
||||
u32 avail_before;
|
||||
u32 freed_before;
|
||||
u32 budget;
|
||||
|
||||
if (cusers->nr_holders > 0) {
|
||||
avail_before = cusers->avail_before;
|
||||
freed_before = cusers->freed_before;
|
||||
} else {
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &avail_before, &freed_before);
|
||||
}
|
||||
|
||||
budget = (cusers->nr_holders + more_holders) * COMMIT_HOLD_ALLOC_BUDGET;
|
||||
|
||||
return avail_before >= budget && freed_before >= budget;
|
||||
}
|
||||
|
||||
static bool hold_commit(struct super_block *sb, struct server_info *server,
|
||||
struct commit_users *cusers, struct commit_hold *hold)
|
||||
{
|
||||
bool held = false;
|
||||
bool has_room;
|
||||
bool held;
|
||||
u32 budget;
|
||||
u32 av;
|
||||
u32 fr;
|
||||
|
||||
spin_lock(&cusers->lock);
|
||||
|
||||
@@ -357,19 +358,39 @@ static bool hold_commit(struct super_block *sb, struct server_info *server,
|
||||
|
||||
check_holder_budget(sb, server, cusers);
|
||||
|
||||
if (cusers->nr_holders == 0) {
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &av, &fr);
|
||||
} else {
|
||||
av = cusers->avail_before;
|
||||
fr = cusers->freed_before;
|
||||
}
|
||||
|
||||
/* +2 for our additional hold and then for the final commit work the server does */
|
||||
if (list_empty(&cusers->applying) && commit_alloc_has_room(server, cusers, 2)) {
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &hold->avail, &hold->freed);
|
||||
budget = (cusers->nr_holders + 2) * COMMIT_HOLD_ALLOC_BUDGET;
|
||||
has_room = av >= budget && fr >= budget;
|
||||
/* checking applying so holders drain once an apply caller starts waiting */
|
||||
held = !cusers->committing && has_room && list_empty(&cusers->applying);
|
||||
|
||||
if (held) {
|
||||
if (cusers->nr_holders == 0) {
|
||||
cusers->avail_before = hold->avail;
|
||||
cusers->freed_before = hold->freed;
|
||||
cusers->avail_before = av;
|
||||
cusers->freed_before = fr;
|
||||
hold->avail = av;
|
||||
hold->freed = fr;
|
||||
cusers->exceeded = false;
|
||||
} else {
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &hold->avail, &hold->freed);
|
||||
}
|
||||
|
||||
hold->exceeded = false;
|
||||
hold->start = ktime_get();
|
||||
list_add_tail(&hold->entry, &cusers->holding);
|
||||
|
||||
cusers->nr_holders++;
|
||||
held = true;
|
||||
|
||||
} else if (!has_room && cusers->nr_holders == 0 && !cusers->committing) {
|
||||
cusers->committing = true;
|
||||
queue_work(server->wq, &server->commit_work);
|
||||
}
|
||||
|
||||
spin_unlock(&cusers->lock);
|
||||
@@ -403,7 +424,6 @@ static int server_apply_commit(struct super_block *sb, struct commit_hold *hold,
|
||||
DECLARE_SERVER_INFO(sb, server);
|
||||
struct commit_users *cusers = &server->cusers;
|
||||
struct timespec ts;
|
||||
bool start_commit;
|
||||
|
||||
spin_lock(&cusers->lock);
|
||||
|
||||
@@ -424,12 +444,14 @@ static int server_apply_commit(struct super_block *sb, struct commit_hold *hold,
|
||||
list_del_init(&hold->entry);
|
||||
hold->ret = err;
|
||||
}
|
||||
cusers->nr_holders--;
|
||||
start_commit = cusers->nr_holders == 0 && !list_empty(&cusers->applying);
|
||||
spin_unlock(&cusers->lock);
|
||||
|
||||
if (start_commit)
|
||||
cusers->nr_holders--;
|
||||
if (cusers->nr_holders == 0 && !cusers->committing && !list_empty(&cusers->applying)) {
|
||||
cusers->committing = true;
|
||||
queue_work(server->wq, &server->commit_work);
|
||||
}
|
||||
|
||||
spin_unlock(&cusers->lock);
|
||||
|
||||
wait_event(cusers->waitq, list_empty_careful(&hold->entry));
|
||||
smp_rmb(); /* entry load before ret */
|
||||
@@ -438,8 +460,8 @@ static int server_apply_commit(struct super_block *sb, struct commit_hold *hold,
|
||||
|
||||
/*
|
||||
* Start a commit from the commit work. We should only have been queued
|
||||
* while a holder is waiting to apply after all active holders have
|
||||
* finished.
|
||||
* while there are no active holders and someone started the commit.
|
||||
* There may or may not be blocked apply callers waiting for the result.
|
||||
*/
|
||||
static int commit_start(struct super_block *sb, struct commit_users *cusers)
|
||||
{
|
||||
@@ -448,7 +470,7 @@ static int commit_start(struct super_block *sb, struct commit_users *cusers)
|
||||
/* make sure holders held off once commit started */
|
||||
spin_lock(&cusers->lock);
|
||||
TRACE_COMMIT_USERS(sb, cusers, start);
|
||||
if (WARN_ON_ONCE(list_empty(&cusers->applying) || cusers->nr_holders != 0))
|
||||
if (WARN_ON_ONCE(!cusers->committing || cusers->nr_holders != 0))
|
||||
ret = -EINVAL;
|
||||
spin_unlock(&cusers->lock);
|
||||
|
||||
@@ -471,6 +493,7 @@ static void commit_end(struct super_block *sb, struct commit_users *cusers, int
|
||||
smp_wmb(); /* ret stores before list updates */
|
||||
list_for_each_entry_safe(hold, tmp, &cusers->applying, entry)
|
||||
list_del_init(&hold->entry);
|
||||
cusers->committing = false;
|
||||
spin_unlock(&cusers->lock);
|
||||
|
||||
wake_up(&cusers->waitq);
|
||||
@@ -543,7 +566,7 @@ static void set_stable_super(struct server_info *server, struct scoutfs_super_bl
|
||||
* implement commits with a single pending work func.
|
||||
*
|
||||
* Processing paths hold the commit while they're making multiple
|
||||
* dependent changes. When they're done and want it persistent they add
|
||||
* dependent changes. When they're done and want it persistent they
|
||||
* queue the commit work. This work runs, performs the commit, and
|
||||
* wakes all the applying waiters with the result. Readers can run
|
||||
* concurrently with these commits.
|
||||
@@ -2058,6 +2081,13 @@ out:
|
||||
* reset the next range key if there's still work to do. If the
|
||||
* operation is complete then we tear down the input log_trees items and
|
||||
* delete the status.
|
||||
*
|
||||
* Processing all the completions can take more than one transaction.
|
||||
* We return -EINPROGRESS if we have to commit a transaction and the
|
||||
* caller will apply the commit and immediate call back in so we can
|
||||
* perform another commit. We need to be very careful to leave the
|
||||
* status in a state where requests won't be issued at the wrong time
|
||||
* (by forcing nr_completions to a batch while we delete them).
|
||||
*/
|
||||
static int splice_log_merge_completions(struct super_block *sb,
|
||||
struct scoutfs_log_merge_status *stat,
|
||||
@@ -2070,15 +2100,29 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
struct scoutfs_log_merge_range rng;
|
||||
struct scoutfs_log_trees lt = {{{0,}}};
|
||||
SCOUTFS_BTREE_ITEM_REF(iref);
|
||||
bool upd_stat = true;
|
||||
int einprogress = 0;
|
||||
struct scoutfs_key key;
|
||||
char *err_str = NULL;
|
||||
u32 alloc_low;
|
||||
u32 tmp;
|
||||
u64 seq;
|
||||
int ret;
|
||||
int err;
|
||||
|
||||
/* musn't rebalance fs tree parents while reqs rely on their key bounds */
|
||||
if (WARN_ON_ONCE(le64_to_cpu(stat->nr_requests) > 0))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Be overly conservative about how low the allocator can get
|
||||
* before we commit. This gives us a lot of work to do in a
|
||||
* commit while also allowing a pretty big smallest allocator to
|
||||
* work with the theoretically unbounded alloc list splicing.
|
||||
*/
|
||||
scoutfs_alloc_meta_remaining(&server->alloc, &alloc_low, &tmp);
|
||||
alloc_low = min(alloc_low, tmp) / 4;
|
||||
|
||||
/*
|
||||
* Splice in all the completed subtrees at the initial parent
|
||||
* blocks in the main fs_tree before rebalancing any of them.
|
||||
@@ -2100,6 +2144,22 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
|
||||
seq = le64_to_cpu(comp.seq);
|
||||
|
||||
/*
|
||||
* Use having cleared the lists as an indication that
|
||||
* we've already set the parents and don't need to dirty
|
||||
* the btree blocks to do it all over again. This is
|
||||
* safe because there is always an fs block that the
|
||||
* merge dirties and frees into the meta_freed list.
|
||||
*/
|
||||
if (comp.meta_avail.ref.blkno == 0 && comp.meta_freed.ref.blkno == 0)
|
||||
continue;
|
||||
|
||||
if (scoutfs_alloc_meta_low(sb, &server->alloc, alloc_low)) {
|
||||
einprogress = -EINPROGRESS;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = scoutfs_btree_set_parent(sb, &server->alloc, &server->wri,
|
||||
&super->fs_root, &comp.start,
|
||||
&comp.root);
|
||||
@@ -2134,6 +2194,14 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we start rebalancing we force the number of completions
|
||||
* to a batch so that requests won't be issued. Once we're done
|
||||
* we clear the completion count and requests can flow again.
|
||||
*/
|
||||
if (le64_to_cpu(stat->nr_complete) < LOG_MERGE_SPLICE_BATCH)
|
||||
stat->nr_complete = cpu_to_le64(LOG_MERGE_SPLICE_BATCH);
|
||||
|
||||
/*
|
||||
* Now with all the parent blocks spliced in, rebalance items
|
||||
* amongst parents that needed to split/join and delete the
|
||||
@@ -2155,6 +2223,12 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
|
||||
seq = le64_to_cpu(comp.seq);
|
||||
|
||||
if (scoutfs_alloc_meta_low(sb, &server->alloc, alloc_low)) {
|
||||
einprogress = -EINPROGRESS;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* balance when there was a remaining key range */
|
||||
if (le64_to_cpu(comp.flags) & SCOUTFS_LOG_MERGE_COMP_REMAIN) {
|
||||
ret = scoutfs_btree_rebalance(sb, &server->alloc,
|
||||
@@ -2194,18 +2268,11 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
}
|
||||
}
|
||||
|
||||
/* update the status once all completes are processed */
|
||||
scoutfs_key_set_zeros(&stat->next_range_key);
|
||||
stat->nr_complete = 0;
|
||||
|
||||
/* update counts and done if there's still ranges to process */
|
||||
if (!no_ranges) {
|
||||
init_log_merge_key(&key, SCOUTFS_LOG_MERGE_STATUS_ZONE, 0, 0);
|
||||
ret = scoutfs_btree_update(sb, &server->alloc, &server->wri,
|
||||
&super->log_merge, &key,
|
||||
stat, sizeof(*stat));
|
||||
if (ret < 0)
|
||||
err_str = "update status";
|
||||
scoutfs_key_set_zeros(&stat->next_range_key);
|
||||
stat->nr_complete = 0;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -2241,6 +2308,12 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
(le64_to_cpu(lt.finalize_seq) < le64_to_cpu(stat->seq))))
|
||||
continue;
|
||||
|
||||
if (scoutfs_alloc_meta_low(sb, &server->alloc, alloc_low)) {
|
||||
einprogress = -EINPROGRESS;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fr.root = lt.item_root;
|
||||
scoutfs_key_set_zeros(&fr.key);
|
||||
fr.seq = cpu_to_le64(scoutfs_server_next_seq(sb));
|
||||
@@ -2274,9 +2347,10 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
}
|
||||
|
||||
le64_add_cpu(&super->inode_count, le64_to_cpu(lt.inode_count_delta));
|
||||
|
||||
}
|
||||
|
||||
/* everything's done, remove the merge operation */
|
||||
upd_stat = false;
|
||||
init_log_merge_key(&key, SCOUTFS_LOG_MERGE_STATUS_ZONE, 0, 0);
|
||||
ret = scoutfs_btree_delete(sb, &server->alloc, &server->wri,
|
||||
&super->log_merge, &key);
|
||||
@@ -2285,12 +2359,23 @@ static int splice_log_merge_completions(struct super_block *sb,
|
||||
else
|
||||
err_str = "deleting merge status item";
|
||||
out:
|
||||
if (upd_stat) {
|
||||
init_log_merge_key(&key, SCOUTFS_LOG_MERGE_STATUS_ZONE, 0, 0);
|
||||
err = scoutfs_btree_update(sb, &server->alloc, &server->wri,
|
||||
&super->log_merge, &key,
|
||||
stat, sizeof(struct scoutfs_log_merge_status));
|
||||
if (err && !ret) {
|
||||
err_str = "updating merge status item";
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
scoutfs_err(sb, "server error %d splicing log merge completion: %s", ret, err_str);
|
||||
|
||||
BUG_ON(ret); /* inconsistent */
|
||||
|
||||
return ret;
|
||||
return ret ?: einprogress;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2465,6 +2550,12 @@ static void server_log_merge_free_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clients regularly ask if there is log merge work to do. We process
|
||||
* completions inline before responding so that we don't create large
|
||||
* delays between completion processing and the next request. We don't
|
||||
* mind if the client get_log_merge request sees high latency, the
|
||||
* blocked caller has nothing else to do.
|
||||
*
|
||||
* This will return ENOENT to the client if there is no work to do.
|
||||
*/
|
||||
static int server_get_log_merge(struct super_block *sb,
|
||||
@@ -2532,14 +2623,22 @@ restart:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* maybe splice now that we know if there's ranges */
|
||||
/* splice if we have a batch or ran out of ranges */
|
||||
no_next = ret == -ENOENT;
|
||||
no_ranges = scoutfs_key_is_zeros(&stat.next_range_key) && ret == -ENOENT;
|
||||
if (le64_to_cpu(stat.nr_requests) == 0 &&
|
||||
(no_next || le64_to_cpu(stat.nr_complete) >= LOG_MERGE_SPLICE_BATCH)) {
|
||||
ret = splice_log_merge_completions(sb, &stat, no_ranges);
|
||||
if (ret < 0)
|
||||
if (ret == -EINPROGRESS) {
|
||||
mutex_unlock(&server->logs_mutex);
|
||||
ret = server_apply_commit(sb, &hold, 0);
|
||||
if (ret < 0)
|
||||
goto respond;
|
||||
server_hold_commit(sb, &hold);
|
||||
mutex_lock(&server->logs_mutex);
|
||||
} else if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
/* splicing resets key and adds ranges, could finish status */
|
||||
goto restart;
|
||||
}
|
||||
@@ -2741,6 +2840,7 @@ out:
|
||||
mutex_unlock(&server->logs_mutex);
|
||||
ret = server_apply_commit(sb, &hold, ret);
|
||||
|
||||
respond:
|
||||
return scoutfs_net_response(sb, conn, cmd, id, ret, &req, sizeof(req));
|
||||
}
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ wrote blk 39
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
55.. 1:
|
||||
@@ -143,10 +142,8 @@ wrote blk 44
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
55.. 1:
|
||||
@@ -167,10 +164,8 @@ wrote blk 48
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -193,10 +188,8 @@ wrote blk 62
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -221,10 +214,8 @@ wrote blk 67
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -252,10 +243,8 @@ wrote blk 73
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -285,10 +274,8 @@ wrote blk 86
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -304,7 +291,6 @@ wrote blk 86
|
||||
73.. 1:
|
||||
74.. 5: unwritten
|
||||
79.. 2:
|
||||
81.. 5: unwritten
|
||||
86.. 1:
|
||||
87.. 2:
|
||||
95.. 1: eof
|
||||
@@ -320,10 +306,8 @@ wrote blk 92
|
||||
25.. 1:
|
||||
26.. 6: unwritten
|
||||
32.. 1:
|
||||
33.. 6: unwritten
|
||||
39.. 1:
|
||||
40.. 1:
|
||||
41.. 3: unwritten
|
||||
44.. 1:
|
||||
45.. 3: unwritten
|
||||
48.. 1:
|
||||
@@ -339,10 +323,8 @@ wrote blk 92
|
||||
73.. 1:
|
||||
74.. 5: unwritten
|
||||
79.. 2:
|
||||
81.. 5: unwritten
|
||||
86.. 1:
|
||||
87.. 2:
|
||||
89.. 3: unwritten
|
||||
92.. 1:
|
||||
93.. 2: unwritten
|
||||
95.. 1: eof
|
||||
|
||||
@@ -168,7 +168,8 @@ print_extents_found $prefix
|
||||
# the start, and one at the end.
|
||||
#
|
||||
# Let's keep this last because it creates a ton of output to read
|
||||
# through.
|
||||
# through. The correct output is tied to preallocation strategy so it
|
||||
# has to be verified each time we change preallocation.
|
||||
#
|
||||
echo "== block writes into region allocs hole"
|
||||
t_set_sysfs_mount_option 0 data_prealloc_blocks 8
|
||||
|
||||
Reference in New Issue
Block a user