--- fs/netfs/Makefile | 1 fs/netfs/internal.h | 24 +++++++++++ fs/netfs/main.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ fs/netfs/objects.c | 6 ++- fs/netfs/write_helper.c | 4 ++ include/linux/netfs.h | 1 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 fs/netfs/main.c diff --git a/fs/netfs/Makefile b/fs/netfs/Makefile index a7c3a9173ac0..62dad3d7bea0 100644 --- a/fs/netfs/Makefile +++ b/fs/netfs/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 netfs-y := \ + main.o \ objects.o \ read_helper.o \ write_back.o \ diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index 381ca64062eb..a9ec6591f90a 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -22,6 +22,30 @@ ssize_t netfs_file_direct_write(struct netfs_dirty_region *region, struct kiocb *iocb, struct iov_iter *from); +/* + * main.c + */ +extern struct list_head netfs_regions; +extern spinlock_t netfs_regions_lock; + +#ifdef CONFIG_PROC_FS +static inline void netfs_proc_add_region(struct netfs_dirty_region *region) +{ + spin_lock(&netfs_regions_lock); + list_add_tail_rcu(®ion->proc_link, &netfs_regions); + spin_unlock(&netfs_regions_lock); +} +static inline void netfs_proc_del_region(struct netfs_dirty_region *region) +{ + spin_lock(&netfs_regions_lock); + list_del_rcu(®ion->proc_link); + spin_unlock(&netfs_regions_lock); +} +#else +static inline void netfs_proc_add_region(struct netfs_dirty_region *region) {} +static inline void netfs_proc_del_region(struct netfs_dirty_region *region) {} +#endif + /* * objects.c */ diff --git a/fs/netfs/main.c b/fs/netfs/main.c new file mode 100644 index 000000000000..125b570efefd --- /dev/null +++ b/fs/netfs/main.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Network filesystem library. + * + * Copyright (C) 2021 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@xxxxxxxxxx) + */ + +#include <linux/module.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include "internal.h" + +#ifdef CONFIG_PROC_FS +LIST_HEAD(netfs_regions); +DEFINE_SPINLOCK(netfs_regions_lock); + +static const char netfs_proc_region_states[] = "PRADFC"; +static const char *netfs_proc_region_types[] = { + [NETFS_REGION_ORDINARY] = "ORD ", + [NETFS_REGION_DIO] = "DIOW", + [NETFS_REGION_DSYNC] = "DSYN", +}; + +/* + * Generate a list of regions in /proc/fs/netfs/regions + */ +static int netfs_regions_seq_show(struct seq_file *m, void *v) +{ + struct netfs_dirty_region *region; + + if (v == &netfs_regions) { + seq_puts(m, + "REGION REF TYPE S FL DEV INODE DIRTY, BOUNDS, RESV\n" + "======== === ==== = == ===== ======== ==============================\n" + ); + return 0; + } + + region = list_entry(v, struct netfs_dirty_region, proc_link); + seq_printf(m, + "%08x %3d %s %c %2lx %02x:%02x %8x %04llx-%04llx %04llx-%04llx %04llx-%04llx\n", + region->debug_id, + refcount_read(®ion->ref), + netfs_proc_region_types[region->type], + netfs_proc_region_states[region->state], + region->flags, + 0, 0, 0, + region->dirty.start, region->dirty.end, + region->bounds.start, region->bounds.end, + region->reserved.start, region->reserved.end); + return 0; +} + +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); +} + +static void *netfs_regions_seq_next(struct seq_file *m, void *v, loff_t *_pos) +{ + return seq_list_next(v, &netfs_regions, _pos); +} + +static void netfs_regions_seq_stop(struct seq_file *m, void *v) + __releases(rcu) +{ + rcu_read_unlock(); +} + +const struct seq_operations netfs_regions_seq_ops = { + .start = netfs_regions_seq_start, + .next = netfs_regions_seq_next, + .stop = netfs_regions_seq_stop, + .show = netfs_regions_seq_show, +}; +#endif /* CONFIG_PROC_FS */ + +static int __init netfs_init(void) +{ + if (!proc_mkdir("fs/netfs", NULL)) + goto error; + + if (!proc_create_seq("fs/netfs/regions", S_IFREG | 0444, NULL, + &netfs_regions_seq_ops)) + goto error_proc; + + return 0; + +error_proc: + remove_proc_entry("fs/netfs", NULL); +error: + return -ENOMEM; +} +fs_initcall(netfs_init); + +static void __exit netfs_exit(void) +{ + remove_proc_entry("fs/netfs", NULL); +} +module_exit(netfs_exit); diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index 8926b4230d91..1149f12ca8c9 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -60,8 +60,10 @@ struct netfs_dirty_region *netfs_alloc_dirty_region(void) struct netfs_dirty_region *region; region = kzalloc(sizeof(struct netfs_dirty_region), GFP_KERNEL); - if (region) + if (region) { + INIT_LIST_HEAD(®ion->proc_link); netfs_stat(&netfs_n_wh_region); + } return region; } @@ -81,6 +83,8 @@ void netfs_free_dirty_region(struct netfs_i_context *ctx, { if (region) { trace_netfs_ref_region(region->debug_id, 0, netfs_region_trace_free); + if (!list_empty(®ion->proc_link)) + netfs_proc_del_region(region); if (ctx->ops->free_dirty_region) ctx->ops->free_dirty_region(region); netfs_put_flush_group(region->group); diff --git a/fs/netfs/write_helper.c b/fs/netfs/write_helper.c index fa048e3882ea..b1fe2d4c0df6 100644 --- a/fs/netfs/write_helper.c +++ b/fs/netfs/write_helper.c @@ -86,10 +86,13 @@ static void netfs_init_dirty_region(struct netfs_dirty_region *region, group = list_last_entry(&ctx->flush_groups, struct netfs_flush_group, group_link); region->group = netfs_get_flush_group(group); + spin_lock(&ctx->lock); list_add_tail(®ion->flush_link, &group->region_list); + spin_unlock(&ctx->lock); } trace_netfs_ref_region(region->debug_id, 1, netfs_region_trace_new); trace_netfs_dirty(ctx, region, NULL, netfs_dirty_trace_new); + netfs_proc_add_region(region); } /* @@ -198,6 +201,7 @@ static struct netfs_dirty_region *netfs_split_dirty_region( list_add(&tail->dirty_link, ®ion->dirty_link); list_add(&tail->flush_link, ®ion->flush_link); trace_netfs_dirty(ctx, tail, region, netfs_dirty_trace_split); + netfs_proc_add_region(tail); return tail; } diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 6acf3fb170c3..43d195badb0d 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -228,6 +228,7 @@ enum netfs_region_type { */ struct netfs_dirty_region { struct netfs_flush_group *group; + struct list_head proc_link; /* Link in /proc/fs/netfs/regions */ struct list_head active_link; /* Link in i_context->pending/active_writes */ struct list_head dirty_link; /* Link in i_context->dirty_regions */ struct list_head flush_link; /* Link in group->region_list or