diff --git a/fs/netfs/buffered_flush.c b/fs/netfs/buffered_flush.c index 74e4eb762304d..35ff908f1ead1 100644 --- a/fs/netfs/buffered_flush.c +++ b/fs/netfs/buffered_flush.c @@ -187,7 +187,7 @@ int netfs_flush_conflicting_writes(struct netfs_inode *ctx, * point indicates the last page in the front region. A pointer to the new * front part is returned. */ -static struct netfs_dirty_region *netfs_split_off_front( +static struct netfs_dirty_region *netfs_alloc_split_off_front( struct netfs_inode *ctx, struct netfs_dirty_region *back, pgoff_t front_last, @@ -195,39 +195,13 @@ static struct netfs_dirty_region *netfs_split_off_front( { struct netfs_dirty_region *front; - BUG_ON(back->first > front_last); - BUG_ON(back->last < front_last); - front = netfs_alloc_dirty_region(GFP_ATOMIC); if (!front) { pr_err("OOM\n"); BUG(); } - front->debug_id = atomic_inc_return(&netfs_region_debug_ids); - front->type = back->type; - front->first = back->first; - front->last = front_last; - back->first = front->last + 1; - front->from = back->from; - back->from = back->first * PAGE_SIZE; - front->to = back->from; - - _debug("front %04lx-%04lx %08llx-%08llx", - front->first, front->last, front->from, front->to); - _debug("back %04lx-%04lx %08llx-%08llx", - back->first, back->last, back->from, back->to); - - _debug("split D=%x from D=%x", front->debug_id, back->debug_id); - - if (ctx->ops->split_dirty_region) - ctx->ops->split_dirty_region(front, back); - - list_move_tail(&front->dirty_link, &back->dirty_link); - list_add_tail (&front->proc_link, &back->proc_link); - - trace_netfs_dirty(ctx, front, back, why); - trace_netfs_dirty(ctx, back, front, netfs_dirty_trace_split); + netfs_split_off_front(ctx, front, back, front_last, why); return front; } @@ -301,8 +275,8 @@ static void netfs_split_out_regions(struct netfs_io_request *wreq, if (wreq->first != region->first) { BUG_ON(wreq->first < region->first); BUG_ON(wreq->first == 0); - netfs_split_off_front(ctx, region, wreq->first - 1, - netfs_dirty_trace_split_off_front); + netfs_alloc_split_off_front(ctx, region, wreq->first - 1, + netfs_dirty_trace_split_off_front); netfs_check_dirty_list('F', &ctx->dirty_regions, region); } @@ -311,7 +285,7 @@ static void netfs_split_out_regions(struct netfs_io_request *wreq, if (wreq->last == region->last) goto excise; if (wreq->last < region->last) { - region = netfs_split_off_front( + region = netfs_alloc_split_off_front( ctx, region, wreq->last, netfs_dirty_trace_split_off_back); if (region->dirty_link.next == &front->dirty_link) diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c index 553b7e9117d14..0ab4b6c396376 100644 --- a/fs/netfs/buffered_write.c +++ b/fs/netfs/buffered_write.c @@ -148,7 +148,8 @@ static bool netfs_try_bridge_next(struct netfs_inode *ctx, if (target->last >= next->last) { /* Next entry is superseded in its entirety. */ list_move(&next->dirty_link, discards); - trace_netfs_dirty(ctx, target, next, netfs_dirty_trace_supersede); + trace_netfs_dirty(ctx, target, next, + netfs_dirty_trace_supersede_all); if (target->last > next->last) goto again; goto out; @@ -156,7 +157,7 @@ static bool netfs_try_bridge_next(struct netfs_inode *ctx, next->from = target->to; next->first = target->last + 1; - trace_netfs_dirty(ctx, target, next, netfs_dirty_trace_supersede); + trace_netfs_dirty(ctx, target, next, netfs_dirty_trace_superseded); out: return true; /* Return true for tail-callers */ } @@ -224,6 +225,74 @@ static bool netfs_merge_with_next(struct netfs_inode *ctx, return true; } +/* + * Insert a new region at the specified point, initialising it from the + * proposed region. + */ +static void netfs_insert_new(struct netfs_inode *ctx, + struct netfs_dirty_region *insertion, + const struct netfs_dirty_region *proposal, + struct file *file, + struct netfs_dirty_region *insert_point, + enum netfs_dirty_trace how) +{ + insertion->first = proposal->first; + insertion->last = proposal->last; + insertion->from = proposal->from; + insertion->to = proposal->to; + insertion->type = proposal->type; + netfs_init_dirty_region(ctx, insertion, file); + switch (how) { + case netfs_dirty_trace_insert_only: + list_add_tail(&insertion->dirty_link, &ctx->dirty_regions); + break; + case netfs_dirty_trace_insert_before: + case netfs_dirty_trace_supersede_front: + list_add_tail(&insertion->dirty_link, &insert_point->dirty_link); + break; + case netfs_dirty_trace_insert_after: + case netfs_dirty_trace_supersede_back: + list_add(&insertion->dirty_link, &insert_point->dirty_link); + break; + default: + BUG_ON(1); + } + trace_netfs_dirty(ctx, insertion, insert_point, how); +} + +/* + * Split the front off of the dirty region at the specified point into the new + * supplied front region, where the point indicates the last page in the front + * region. + */ +void netfs_split_off_front(struct netfs_inode *ctx, + struct netfs_dirty_region *front, + struct netfs_dirty_region *back, + pgoff_t front_last, + enum netfs_dirty_trace why) +{ + BUG_ON(back->first > front_last); + BUG_ON(back->last < front_last); + + front->debug_id = atomic_inc_return(&netfs_region_debug_ids); + front->type = back->type; + front->first = back->first; + front->last = front_last; + back->first = front->last + 1; + front->from = back->from; + back->from = back->first * PAGE_SIZE; + front->to = back->from; + + if (front->type != NETFS_COPY_TO_CACHE && + ctx->ops->split_dirty_region) + ctx->ops->split_dirty_region(front, back); + + list_move_tail(&front->dirty_link, &back->dirty_link); + list_add(&front->proc_link, &back->proc_link); + + trace_netfs_dirty(ctx, front, back, why); +} + /* * Supersede some data that's marked copy-to-cache only. We may need to make * up to two splits in the region and we may need to merge with the adjacent @@ -237,7 +306,7 @@ static void netfs_supersede_cache_copy(struct netfs_inode *ctx, { struct netfs_dirty_region *prev = netfs_prev_region(ctx, target); struct netfs_dirty_region *next = netfs_next_region(ctx, target); - struct netfs_dirty_region *tail, *insertion; + struct netfs_dirty_region *insertion, *front; _enter("D=%u", target->debug_id); @@ -286,23 +355,20 @@ static void netfs_supersede_cache_copy(struct netfs_inode *ctx, prev->last = proposal->last; target->first = proposal->last + 1; target->from = target->first * PAGE_SIZE; - trace_netfs_dirty(ctx, prev, target, netfs_dirty_trace_merged_prev_super); + trace_netfs_dirty(ctx, prev, target, + netfs_dirty_trace_merged_prev_super); } else { insertion = netfs_alloc_dirty_region(GFP_ATOMIC); if (!insertion) { pr_err("OOM"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move_tail(&insertion->dirty_link, &target->dirty_link); + netfs_insert_new(ctx, insertion, proposal, file, target, + netfs_dirty_trace_supersede_front); target->first = insertion->last + 1; target->to = target->first * PAGE_SIZE; - trace_netfs_dirty(ctx, insertion, target, netfs_dirty_trace_supersede); + trace_netfs_dirty(ctx, insertion, target, + netfs_dirty_trace_superseded); } return; } @@ -318,23 +384,20 @@ static void netfs_supersede_cache_copy(struct netfs_inode *ctx, next->first = proposal->first; target->last = proposal->first - 1; target->to = proposal->first * PAGE_SIZE; - trace_netfs_dirty(ctx, next, target, netfs_dirty_trace_merged_next_super); + trace_netfs_dirty(ctx, next, target, + netfs_dirty_trace_merged_next_super); } else { insertion = netfs_alloc_dirty_region(GFP_ATOMIC); if (!insertion) { pr_err("OOM"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move(&insertion->dirty_link, &target->dirty_link); + netfs_insert_new(ctx, insertion, proposal, file, target, + netfs_dirty_trace_supersede_back); target->first = proposal->last + 1; target->from = target->first * PAGE_SIZE; - trace_netfs_dirty(ctx, insertion, target, netfs_dirty_trace_supersede); + trace_netfs_dirty(ctx, target, insertion, + netfs_dirty_trace_superseded); } return; } @@ -342,33 +405,22 @@ static void netfs_supersede_cache_copy(struct netfs_inode *ctx, /* Otherwise we have to split the copy-to-cache region and insert the * proposed region between. */ - tail = netfs_alloc_dirty_region(GFP_ATOMIC); - if (!tail) { + insertion = netfs_alloc_dirty_region(GFP_ATOMIC); + if (!insertion) { pr_err("OOM"); BUG(); } - tail->first = proposal->last + 1; - tail->last = target->last; - tail->from = tail->first * PAGE_SIZE; - tail->to = target->to; - tail->type = NETFS_COPY_TO_CACHE; - netfs_init_dirty_region(ctx, tail, NULL); - list_move(&tail->dirty_link, &target->dirty_link); - trace_netfs_dirty(ctx, tail, target, netfs_dirty_trace_split_c2c); - - insertion = netfs_alloc_dirty_region(GFP_ATOMIC); - if (!insertion) { + front = netfs_alloc_dirty_region(GFP_ATOMIC); + if (!front) { pr_err("OOM"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move(&insertion->dirty_link, &target->dirty_link); - trace_netfs_dirty(ctx, insertion, target, netfs_dirty_trace_supersede); + + netfs_split_off_front(ctx, front, target, proposal->first - 1, + netfs_dirty_trace_split_c2c); + + netfs_insert_new(ctx, insertion, proposal, file, target, + netfs_dirty_trace_supersede_front); target->from = min(target->from, insertion->from); target->first = min(target->first, insertion->first); @@ -406,14 +458,8 @@ static void netfs_commit_region(struct netfs_inode *ctx, struct file *file, pr_err("OOM\n"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move_tail(&insertion->dirty_link, &ctx->dirty_regions); - trace_netfs_dirty(ctx, insertion, NULL, netfs_dirty_trace_insert); + netfs_insert_new(ctx, insertion, proposal, file, NULL, + netfs_dirty_trace_insert_only); goto done; } @@ -448,14 +494,8 @@ static void netfs_commit_region(struct netfs_inode *ctx, struct file *file, pr_err("OOM\n"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move_tail(&insertion->dirty_link, &ctx->dirty_regions); - trace_netfs_dirty(ctx, insertion, NULL, netfs_dirty_trace_insert); + netfs_insert_new(ctx, insertion, proposal, file, target, + netfs_dirty_trace_insert_after); goto done; } target = next; @@ -471,14 +511,8 @@ static void netfs_commit_region(struct netfs_inode *ctx, struct file *file, pr_err("OOM\n"); BUG(); } - insertion->first = proposal->first; - insertion->last = proposal->last; - insertion->from = proposal->from; - insertion->to = proposal->to; - insertion->type = proposal->type; - netfs_init_dirty_region(ctx, insertion, file); - list_move_tail(&insertion->dirty_link, &target->dirty_link); - trace_netfs_dirty(ctx, insertion, NULL, netfs_dirty_trace_insert); + netfs_insert_new(ctx, insertion, proposal, file, target, + netfs_dirty_trace_insert_before); done: spin_unlock(&ctx->dirty_lock); diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 82bb8e59be201..c1b000f5a1124 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -32,8 +32,6 @@ int netfs_prefetch_for_write(struct file *file, struct folio *folio, size_t len) /* * buffered_write.c */ -int netfs_preallocate_regions(struct list_head *spare_regions); -void netfs_unpreallocate_regions(struct list_head *spare_regions); void netfs_discard_regions(struct netfs_inode *ctx, struct list_head *discards, enum netfs_region_trace why); @@ -42,6 +40,11 @@ bool netfs_are_regions_mergeable(struct netfs_inode *ctx, const struct netfs_dirty_region *b); struct netfs_dirty_region *netfs_find_region(struct netfs_inode *ctx, pgoff_t first, pgoff_t last); +void netfs_split_off_front(struct netfs_inode *ctx, + struct netfs_dirty_region *front, + struct netfs_dirty_region *back, + pgoff_t front_last, + enum netfs_dirty_trace why); void netfs_rreq_do_write_to_cache(struct netfs_io_request *rreq); /* diff --git a/fs/netfs/main.c b/fs/netfs/main.c index 24449361345e2..117e94f9968bc 100644 --- a/fs/netfs/main.c +++ b/fs/netfs/main.c @@ -71,12 +71,12 @@ static void *netfs_requests_seq_start(struct seq_file *m, loff_t *_pos) __acquires(rcu) { rcu_read_lock(); - return seq_list_start_head(&netfs_io_requests, *_pos); + return seq_list_start_head_rcu(&netfs_io_requests, *_pos); } static void *netfs_requests_seq_next(struct seq_file *m, void *v, loff_t *_pos) { - return seq_list_next(v, &netfs_io_requests, _pos); + return seq_list_next_rcu(v, &netfs_io_requests, _pos); } static void netfs_requests_seq_stop(struct seq_file *m, void *v) @@ -122,12 +122,12 @@ static void *netfs_regions_seq_start(struct seq_file *m, loff_t *_pos) __acquires(rcu) { rcu_read_lock(); - return seq_list_start_head(&netfs_regions, *_pos); + return seq_list_start_head_rcu(&netfs_regions, *_pos); } static void *netfs_regions_seq_next(struct seq_file *m, void *v, loff_t *_pos) { - return seq_list_next(v, &netfs_regions, _pos); + return seq_list_next_rcu(v, &netfs_regions, _pos); } static void netfs_regions_seq_stop(struct seq_file *m, void *v) diff --git a/include/trace/events/netfs.h b/include/trace/events/netfs.h index c1da3bb8c2116..82fc3ad927bc9 100644 --- a/include/trace/events/netfs.h +++ b/include/trace/events/netfs.h @@ -128,7 +128,9 @@ EM(netfs_dirty_trace_flush2, "FLUSH! ") \ EM(netfs_dirty_trace_flush_conflict, "FLSH CONFL") \ EM(netfs_dirty_trace_flush_dsync, "FLSH DSYNC") \ - EM(netfs_dirty_trace_insert, "INSERT ") \ + EM(netfs_dirty_trace_insert_only, "INS ONLY ") \ + EM(netfs_dirty_trace_insert_after, "INS AFTER ") \ + EM(netfs_dirty_trace_insert_before, "INS BEFORE") \ EM(netfs_dirty_trace_mark_copy_to_cache,"COPY2CACHE") \ EM(netfs_dirty_trace_merged_next, "MERGE NEXT") \ EM(netfs_dirty_trace_merged_next_super, "MRG NXT SU") \ @@ -141,7 +143,9 @@ EM(netfs_dirty_trace_split_off_back, "SPLIT BACK") \ EM(netfs_dirty_trace_split_off_front, "SPLIT FRNT") \ EM(netfs_dirty_trace_superseded, "SUPERSEDED") \ - EM(netfs_dirty_trace_supersede, "SUPERSEDE ") \ + EM(netfs_dirty_trace_supersede_all, "SUPRSD ALL") \ + EM(netfs_dirty_trace_supersede_back, "SUPRSD BAK") \ + EM(netfs_dirty_trace_supersede_front, "SUPRSD FRN") \ E_(netfs_dirty_trace_wait_active, "WAIT ACTV ") #define netfs_folio_traces \