diff --git a/kmod/src/Makefile b/kmod/src/Makefile index 565b2ee7..efd078c4 100644 --- a/kmod/src/Makefile +++ b/kmod/src/Makefile @@ -42,6 +42,7 @@ scoutfs-y += \ srch.o \ super.o \ sysfs.o \ + totl.o \ trans.o \ triggers.o \ tseq.o \ diff --git a/kmod/src/counters.h b/kmod/src/counters.h index 42deed1d..72521b24 100644 --- a/kmod/src/counters.h +++ b/kmod/src/counters.h @@ -199,10 +199,7 @@ EXPAND_COUNTER(srch_read_stale) \ EXPAND_COUNTER(statfs) \ EXPAND_COUNTER(totl_read_copied) \ - EXPAND_COUNTER(totl_read_finalized) \ - EXPAND_COUNTER(totl_read_fs) \ EXPAND_COUNTER(totl_read_item) \ - EXPAND_COUNTER(totl_read_logged) \ EXPAND_COUNTER(trans_commit_data_alloc_low) \ EXPAND_COUNTER(trans_commit_dirty_meta_full) \ EXPAND_COUNTER(trans_commit_fsync) \ diff --git a/kmod/src/ioctl.c b/kmod/src/ioctl.c index 6bf70403..0ae2a3d7 100644 --- a/kmod/src/ioctl.c +++ b/kmod/src/ioctl.c @@ -42,6 +42,7 @@ #include "alloc.h" #include "server.h" #include "counters.h" +#include "totl.h" #include "scoutfs_trace.h" /* @@ -1038,15 +1039,7 @@ out: struct xattr_total_entry { struct rb_node node; struct scoutfs_ioctl_xattr_total xt; - u64 fs_seq; - u64 fs_total; - u64 fs_count; - u64 fin_seq; - u64 fin_total; - s64 fin_count; - u64 log_seq; - u64 log_total; - s64 log_count; + struct scoutfs_totl_merging merg; }; static int cmp_xt_entry_name(const struct xattr_total_entry *a, @@ -1068,7 +1061,6 @@ static int cmp_xt_entry_name(const struct xattr_total_entry *a, static int read_xattr_total_item(struct super_block *sb, struct scoutfs_key *key, u64 seq, u8 flags, void *val, int val_len, int fic, void *arg) { - struct scoutfs_xattr_totl_val *tval = val; struct xattr_total_entry *ent; struct xattr_total_entry rd; struct rb_root *root = arg; @@ -1105,24 +1097,13 @@ static int read_xattr_total_item(struct super_block *sb, struct scoutfs_key *key return -ENOMEM; memcpy(&ent->xt.name, &rd.xt.name, sizeof(ent->xt.name)); + scoutfs_totl_merge_init(&ent->merg); rb_link_node(&ent->node, parent, node); rb_insert_color(&ent->node, root); } - if (fic & FIC_FS_ROOT) { - ent->fs_seq = seq; - ent->fs_total = le64_to_cpu(tval->total); - ent->fs_count = le64_to_cpu(tval->count); - } else if (fic & FIC_FINALIZED) { - ent->fin_seq = seq; - ent->fin_total += le64_to_cpu(tval->total); - ent->fin_count += le64_to_cpu(tval->count); - } else { - ent->log_seq = seq; - ent->log_total += le64_to_cpu(tval->total); - ent->log_count += le64_to_cpu(tval->count); - } + scoutfs_totl_merge_contribute(&ent->merg, seq, flags, val, val_len, fic); scoutfs_inc_counter(sb, totl_read_item); @@ -1162,30 +1143,6 @@ static void free_all_xt_ents(struct rb_root *root) * have been committed. It doesn't use locking to force commits and * block writers so it can be a little bit out of date with respect to * dirty xattrs in memory across the system. - * - * Our reader has to be careful because the log btree merging code can - * write partial results to the fs_root. This means that a reader can - * see both cases where new finalized logs should be applied to the old - * fs items and where old finalized logs have already been applied to - * the partially merged fs items. Currently active logged items are - * always applied on top of all cases. - * - * These cases are differentiated with a combination of sequence numbers - * in items, the count of contributing xattrs, and a flag - * differentiating finalized and active logged items. This lets us - * recognize all cases, including when finalized logs were merged and - * deleted the fs item. - * - * We're allocating a tracking struct for each totl name we see while - * traversing the item btrees. The forest reader is providing the items - * it finds in leaf blocks that contain the search key. In the worst - * case all of these blocks are full and none of the items overlap. At - * most, figure order a thousand names per mount. But in practice many - * of these factors fall away: leaf blocks aren't fill, leaf items - * overlap, there aren't finalized log btrees, and not all mounts are - * actively changing totals. We're much more likely to only read a - * leaf block's worth of totals that have been long since merged into - * the fs_root. */ static long scoutfs_ioc_read_xattr_totals(struct file *file, unsigned long arg) { @@ -1273,27 +1230,7 @@ static long scoutfs_ioc_read_xattr_totals(struct file *file, unsigned long arg) if (rxt.totals_bytes < sizeof(ent->xt)) break; - /* start with the fs item if we have it */ - if (ent->fs_seq != 0) { - ent->xt.total = ent->fs_total; - ent->xt.count = ent->fs_count; - scoutfs_inc_counter(sb, totl_read_fs); - } - - /* apply finalized logs if they're newer or creating */ - if (((ent->fs_seq != 0) && (ent->fin_seq > ent->fs_seq)) || - ((ent->fs_seq == 0) && (ent->fin_count > 0))) { - ent->xt.total += ent->fin_total; - ent->xt.count += ent->fin_count; - scoutfs_inc_counter(sb, totl_read_finalized); - } - - /* always apply active logs which must be newer than fs and finalized */ - if (ent->log_seq > 0) { - ent->xt.total += ent->log_total; - ent->xt.count += ent->log_count; - scoutfs_inc_counter(sb, totl_read_logged); - } + scoutfs_totl_merge_resolve(&ent->merg, &ent->xt.total, &ent->xt.count); if (ent->xt.total != 0 || ent->xt.count != 0) { if (copy_to_user(uxt, &ent->xt, sizeof(ent->xt))) { diff --git a/kmod/src/lock.c b/kmod/src/lock.c index db4c384f..b3c6b957 100644 --- a/kmod/src/lock.c +++ b/kmod/src/lock.c @@ -36,6 +36,7 @@ #include "item.h" #include "omap.h" #include "util.h" +#include "totl.h" /* * scoutfs uses a lock service to manage item cache consistency between @@ -1244,10 +1245,7 @@ int scoutfs_lock_xattr_totl(struct super_block *sb, enum scoutfs_lock_mode mode, struct scoutfs_key start; struct scoutfs_key end; - scoutfs_key_set_zeros(&start); - start.sk_zone = SCOUTFS_XATTR_TOTL_ZONE; - scoutfs_key_set_ones(&end); - end.sk_zone = SCOUTFS_XATTR_TOTL_ZONE; + scoutfs_totl_set_range(&start, &end); return lock_key_range(sb, mode, flags, &start, &end, lock); } diff --git a/kmod/src/totl.c b/kmod/src/totl.c new file mode 100644 index 00000000..cfa9b31a --- /dev/null +++ b/kmod/src/totl.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 Versity Software, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License v2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include +#include + +#include "format.h" +#include "forest.h" +#include "totl.h" + +void scoutfs_totl_set_range(struct scoutfs_key *start, struct scoutfs_key *end) +{ + scoutfs_key_set_zeros(start); + start->sk_zone = SCOUTFS_XATTR_TOTL_ZONE; + scoutfs_key_set_ones(end); + end->sk_zone = SCOUTFS_XATTR_TOTL_ZONE; +} + +void scoutfs_totl_merge_init(struct scoutfs_totl_merging *merg) +{ + memset(merg, 0, sizeof(struct scoutfs_totl_merging)); +} + +void scoutfs_totl_merge_contribute(struct scoutfs_totl_merging *merg, + u64 seq, u8 flags, void *val, int val_len, int fic) +{ + struct scoutfs_xattr_totl_val *tval = val; + + if (fic & FIC_FS_ROOT) { + merg->fs_seq = seq; + merg->fs_total = le64_to_cpu(tval->total); + merg->fs_count = le64_to_cpu(tval->count); + } else if (fic & FIC_FINALIZED) { + merg->fin_seq = seq; + merg->fin_total += le64_to_cpu(tval->total); + merg->fin_count += le64_to_cpu(tval->count); + } else { + merg->log_seq = seq; + merg->log_total += le64_to_cpu(tval->total); + merg->log_count += le64_to_cpu(tval->count); + } +} + +/* + * .totl. item merging has to be careful because the log btree merging + * code can write partial results to the fs_root. This means that a + * reader can see both cases where new finalized logs should be applied + * to the old fs items and where old finalized logs have already been + * applied to the partially merged fs items. Currently active logged + * items are always applied on top of all cases. + * + * These cases are differentiated with a combination of sequence numbers + * in items, the count of contributing xattrs, and a flag + * differentiating finalized and active logged items. This lets us + * recognize all cases, including when finalized logs were merged and + * deleted the fs item. + */ +void scoutfs_totl_merge_resolve(struct scoutfs_totl_merging *merg, __u64 *total, __u64 *count) +{ + *total = 0; + *count = 0; + + /* start with the fs item if we have it */ + if (merg->fs_seq != 0) { + *total = merg->fs_total; + *count = merg->fs_count; + } + + /* apply finalized logs if they're newer or creating */ + if (((merg->fs_seq != 0) && (merg->fin_seq > merg->fs_seq)) || + ((merg->fs_seq == 0) && (merg->fin_count > 0))) { + *total += merg->fin_total; + *count += merg->fin_count; + } + + /* always apply active logs which must be newer than fs and finalized */ + if (merg->log_seq > 0) { + *total += merg->log_total; + *count += merg->log_count; + } +} diff --git a/kmod/src/totl.h b/kmod/src/totl.h new file mode 100644 index 00000000..52de1fd6 --- /dev/null +++ b/kmod/src/totl.h @@ -0,0 +1,24 @@ +#ifndef _SCOUTFS_TOTL_H_ +#define _SCOUTFS_TOTL_H_ + +#include "key.h" + +struct scoutfs_totl_merging { + u64 fs_seq; + u64 fs_total; + u64 fs_count; + u64 fin_seq; + u64 fin_total; + s64 fin_count; + u64 log_seq; + u64 log_total; + s64 log_count; +}; + +void scoutfs_totl_set_range(struct scoutfs_key *start, struct scoutfs_key *end); +void scoutfs_totl_merge_init(struct scoutfs_totl_merging *merg); +void scoutfs_totl_merge_contribute(struct scoutfs_totl_merging *merg, + u64 seq, u8 flags, void *val, int val_len, int fic); +void scoutfs_totl_merge_resolve(struct scoutfs_totl_merging *merg, __u64 *total, __u64 *count); + +#endif