diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index a129f0952d0b..d22ae5ad4e38 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -75,7 +75,7 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb, INIT_DELAYED_WORK(&mi->mi_zstd_cleanup_work, zstd_free_workspace); mutex_init(&mi->mi_le_mutex); - node = incfs_add_sysfs_node(options->sysfs_name); + node = incfs_add_sysfs_node(options->sysfs_name, mi); if (IS_ERR(node)) { error = PTR_ERR(node); goto err; @@ -130,13 +130,25 @@ int incfs_realloc_mount_info(struct mount_info *mi, kfree(old_buffer); } - if ((options->sysfs_name && !mi->mi_sysfs_node) || - (!options->sysfs_name && mi->mi_sysfs_node) || - (options->sysfs_name && + if (options->sysfs_name && !mi->mi_sysfs_node) + mi->mi_sysfs_node = incfs_add_sysfs_node(options->sysfs_name, + mi); + else if (!options->sysfs_name && mi->mi_sysfs_node) { + incfs_free_sysfs_node(mi->mi_sysfs_node); + mi->mi_sysfs_node = NULL; + } else if (options->sysfs_name && strcmp(options->sysfs_name, - kobject_name(&mi->mi_sysfs_node->isn_sysfs_node)))) { - pr_err("incfs: Can't change sysfs_name mount option on remount\n"); - return -EOPNOTSUPP; + kobject_name(&mi->mi_sysfs_node->isn_sysfs_node))) { + incfs_free_sysfs_node(mi->mi_sysfs_node); + mi->mi_sysfs_node = incfs_add_sysfs_node(options->sysfs_name, + mi); + } + + if (IS_ERR(mi->mi_sysfs_node)) { + int err = PTR_ERR(mi->mi_sysfs_node); + + mi->mi_sysfs_node = NULL; + return err; } mi->mi_options = *options; @@ -1232,18 +1244,15 @@ static int wait_for_data_block(struct data_file *df, int block_index, if (error) return error; - if (!mi->mi_sysfs_node) - return 0; - if (delayed_pending) { - mi->mi_sysfs_node->isn_reads_delayed_pending++; - mi->mi_sysfs_node->isn_reads_delayed_pending_us += + mi->mi_reads_delayed_pending++; + mi->mi_reads_delayed_pending_us += delayed_pending_us; } if (delayed_min_us) { - mi->mi_sysfs_node->isn_reads_delayed_min++; - mi->mi_sysfs_node->isn_reads_delayed_min_us += delayed_min_us; + mi->mi_reads_delayed_min++; + mi->mi_reads_delayed_min_us += delayed_min_us; } return 0; @@ -1334,16 +1343,14 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f, log_block_read(mi, &df->df_id, index); out: - if (mi->mi_sysfs_node) { - if (result == -ETIME) - mi->mi_sysfs_node->isn_reads_failed_timed_out++; - else if (result == -EBADMSG) - mi->mi_sysfs_node->isn_reads_failed_hash_verification++; - else if (result < 0) - mi->mi_sysfs_node->isn_reads_failed_other++; + if (result == -ETIME) + mi->mi_reads_failed_timed_out++; + else if (result == -EBADMSG) + mi->mi_reads_failed_hash_verification++; + else if (result < 0) + mi->mi_reads_failed_other++; - incfs_update_sysfs_error(f, index, result, mi, df); - } + incfs_update_sysfs_error(f, index, result, mi, df); return result; } diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index 21458f2609c7..ca755169de60 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -199,6 +199,37 @@ struct mount_info { u64 mi_le_time_us; u32 mi_le_page; u32 mi_le_errno; + + /* Number of reads timed out */ + u32 mi_reads_failed_timed_out; + + /* Number of reads failed because hash verification failed */ + u32 mi_reads_failed_hash_verification; + + /* Number of reads failed for another reason */ + u32 mi_reads_failed_other; + + /* Number of reads delayed because page had to be fetched */ + u32 mi_reads_delayed_pending; + + /* Total time waiting for pages to be fetched */ + u64 mi_reads_delayed_pending_us; + + /* + * Number of reads delayed because of per-uid min_time_us or + * min_pending_time_us settings + */ + u32 mi_reads_delayed_min; + + /* Total time waiting because of per-uid min_time_us or + * min_pending_time_us settings. + * + * Note that if a read is initially delayed because we have to wait for + * the page, then further delayed because of min_pending_time_us + * setting, this counter gets incremented by only the further delay + * time. + */ + u64 mi_reads_delayed_min_us; }; struct data_file_block { diff --git a/fs/incfs/sysfs.c b/fs/incfs/sysfs.c index 27aee9ed4341..360f03ca9b63 100644 --- a/fs/incfs/sysfs.c +++ b/fs/incfs/sysfs.c @@ -100,7 +100,7 @@ static ssize_t name##_show(struct kobject *kobj, \ struct incfs_sysfs_node *node = container_of(kobj, \ struct incfs_sysfs_node, isn_sysfs_node); \ \ - return sysfs_emit(buff, "%d\n", node->isn_##name); \ + return sysfs_emit(buff, "%d\n", node->isn_mi->mi_##name); \ } \ \ static struct kobj_attribute name##_attr = __ATTR_RO(name) @@ -112,7 +112,7 @@ static ssize_t name##_show(struct kobject *kobj, \ struct incfs_sysfs_node *node = container_of(kobj, \ struct incfs_sysfs_node, isn_sysfs_node); \ \ - return sysfs_emit(buff, "%lld\n", node->isn_##name); \ + return sysfs_emit(buff, "%lld\n", node->isn_mi->mi_##name); \ } \ \ static struct kobj_attribute name##_attr = __ATTR_RO(name) @@ -141,7 +141,7 @@ static void incfs_sysfs_release(struct kobject *kobj) struct incfs_sysfs_node *node = container_of(kobj, struct incfs_sysfs_node, isn_sysfs_node); - kfree(node); + complete(&node->isn_completion); } static const struct attribute_group mount_attr_group = { @@ -153,7 +153,8 @@ static struct kobj_type incfs_kobj_node_ktype = { .release = &incfs_sysfs_release, }; -struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name) +struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name, + struct mount_info *mi) { struct incfs_sysfs_node *node = NULL; int error; @@ -165,6 +166,9 @@ struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name) if (!node) return ERR_PTR(-ENOMEM); + node->isn_mi = mi; + + init_completion(&node->isn_completion); kobject_init(&node->isn_sysfs_node, &incfs_kobj_node_ktype); error = kobject_add(&node->isn_sysfs_node, instances_node, "%s", name); if (error) @@ -192,4 +196,6 @@ void incfs_free_sysfs_node(struct incfs_sysfs_node *node) sysfs_remove_group(&node->isn_sysfs_node, &mount_attr_group); kobject_put(&node->isn_sysfs_node); + wait_for_completion_interruptible(&node->isn_completion); + kfree(node); } diff --git a/fs/incfs/sysfs.h b/fs/incfs/sysfs.h index 446813fac205..65bf55463738 100644 --- a/fs/incfs/sysfs.h +++ b/fs/incfs/sysfs.h @@ -8,41 +8,15 @@ struct incfs_sysfs_node { struct kobject isn_sysfs_node; - /* Number of reads timed out */ - u32 isn_reads_failed_timed_out; + struct completion isn_completion; - /* Number of reads failed because hash verification failed */ - u32 isn_reads_failed_hash_verification; - - /* Number of reads failed for another reason */ - u32 isn_reads_failed_other; - - /* Number of reads delayed because page had to be fetched */ - u32 isn_reads_delayed_pending; - - /* Total time waiting for pages to be fetched */ - u64 isn_reads_delayed_pending_us; - - /* - * Number of reads delayed because of per-uid min_time_us or - * min_pending_time_us settings - */ - u32 isn_reads_delayed_min; - - /* Total time waiting because of per-uid min_time_us or - * min_pending_time_us settings. - * - * Note that if a read is initially delayed because we have to wait for - * the page, then further delayed because of min_pending_time_us - * setting, this counter gets incremented by only the further delay - * time. - */ - u64 isn_reads_delayed_min_us; + struct mount_info *isn_mi; }; int incfs_init_sysfs(void); void incfs_cleanup_sysfs(void); -struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name); +struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name, + struct mount_info *mi); void incfs_free_sysfs_node(struct incfs_sysfs_node *node); #endif diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index 67541b690183..0650ee90f8b1 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -23,6 +23,7 @@ #include "format.h" #include "internal.h" #include "pseudo_files.h" +#include "sysfs.h" #include "verity.h" static int incfs_remount_fs(struct super_block *sb, int *flags, char *data); @@ -1895,5 +1896,9 @@ static int show_options(struct seq_file *m, struct dentry *root) } if (mi->mi_options.report_uid) seq_puts(m, ",report_uid"); + + if (mi->mi_sysfs_node) + seq_printf(m, ",sysfs_name=%s", + kobject_name(&mi->mi_sysfs_node->isn_sysfs_node)); return 0; } diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index 468401a85710..4c30aec3f545 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -4527,6 +4527,88 @@ static int sysfs_test(const char *mount_dir) return result; } +static int sysfs_test_directories(bool one_present, bool two_present) +{ + int result = TEST_FAILURE; + struct stat st; + + TESTEQUAL(stat("/sys/fs/incremental-fs/instances/1", &st), + one_present ? 0 : -1); + if (one_present) + TESTCOND(S_ISDIR(st.st_mode)); + else + TESTEQUAL(errno, ENOENT); + TESTEQUAL(stat("/sys/fs/incremental-fs/instances/2", &st), + two_present ? 0 : -1); + if (two_present) + TESTCOND(S_ISDIR(st.st_mode)); + else + TESTEQUAL(errno, ENOENT); + + result = TEST_SUCCESS; +out: + return result; +} + +static int sysfs_rename_test(const char *mount_dir) +{ + int result = TEST_FAILURE; + char *backing_dir = NULL; + char *mount_dir2 = NULL; + int fd = -1; + char c; + + /* Mount with no node */ + TEST(backing_dir = create_backing_dir(mount_dir), backing_dir); + TESTEQUAL(mount_fs(mount_dir, backing_dir, 0), 0); + TESTEQUAL(sysfs_test_directories(false, false), 0); + + /* Remount with node */ + TESTEQUAL(mount_fs_opt(mount_dir, backing_dir, "sysfs_name=1", true), + 0); + TESTEQUAL(sysfs_test_directories(true, false), 0); + TEST(fd = open("/sys/fs/incremental-fs/instances/1/reads_delayed_min", + O_RDONLY | O_CLOEXEC), fd != -1); + TESTEQUAL(pread(fd, &c, 1, 0), 1); + TESTEQUAL(c, '0'); + TESTEQUAL(pread(fd, &c, 1, 0), 1); + TESTEQUAL(c, '0'); + + /* Rename node */ + TESTEQUAL(mount_fs_opt(mount_dir, backing_dir, "sysfs_name=2", true), + 0); + TESTEQUAL(sysfs_test_directories(false, true), 0); + TESTEQUAL(pread(fd, &c, 1, 0), -1); + + /* Try mounting another instance with same node name */ + TEST(mount_dir2 = concat_file_name(backing_dir, "incfs-mount-dir2"), + mount_dir2); + rmdir(mount_dir2); /* In case we crashed before */ + TESTSYSCALL(mkdir(mount_dir2, 0777)); + TEST(mount_fs_opt(mount_dir2, backing_dir, "sysfs_name=2", false), + -1); + + /* Try mounting another instance then remounting with existing name */ + TESTEQUAL(mount_fs(mount_dir2, backing_dir, 0), 0); + TESTEQUAL(mount_fs_opt(mount_dir2, backing_dir, "sysfs_name=2", true), + -1); + + /* Remount with no node */ + TESTEQUAL(mount_fs_opt(mount_dir, backing_dir, "", true), + 0); + TESTEQUAL(sysfs_test_directories(false, false), 0); + + result = TEST_SUCCESS; +out: + umount(mount_dir2); + rmdir(mount_dir2); + free(mount_dir2); + close(fd); + umount(mount_dir); + free(backing_dir); + return result; +} + static char *setup_mount_dir() { struct stat st; @@ -4647,6 +4729,7 @@ int main(int argc, char *argv[]) MAKE_TEST(truncate_test), MAKE_TEST(stat_test), MAKE_TEST(sysfs_test), + MAKE_TEST(sysfs_rename_test), }; #undef MAKE_TEST