// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2021 Google LLC */ #include #include #include #include "sysfs.h" #include "data_mgmt.h" #include "vfs.h" /****************************************************************************** * Define sys/fs/incrementalfs & sys/fs/incrementalfs/features *****************************************************************************/ #define INCFS_NODE_FEATURES "features" #define INCFS_NODE_INSTANCES "instances" static struct kobject *sysfs_root; static struct kobject *features_node; static struct kobject *instances_node; #define DECLARE_FEATURE_FLAG(name) \ static ssize_t name##_show(struct kobject *kobj, \ struct kobj_attribute *attr, char *buff) \ { \ return sysfs_emit(buff, "supported\n"); \ } \ \ static struct kobj_attribute name##_attr = __ATTR_RO(name) DECLARE_FEATURE_FLAG(corefs); DECLARE_FEATURE_FLAG(zstd); DECLARE_FEATURE_FLAG(v2); static struct attribute *attributes[] = { &corefs_attr.attr, &zstd_attr.attr, &v2_attr.attr, NULL, }; static const struct attribute_group attr_group = { .attrs = attributes, }; int __init incfs_init_sysfs(void) { int res = -ENOMEM; sysfs_root = kobject_create_and_add(INCFS_NAME, fs_kobj); if (!sysfs_root) return -ENOMEM; instances_node = kobject_create_and_add(INCFS_NODE_INSTANCES, sysfs_root); if (!instances_node) goto err_put_root; features_node = kobject_create_and_add(INCFS_NODE_FEATURES, sysfs_root); if (!features_node) goto err_put_instances; res = sysfs_create_group(features_node, &attr_group); if (res) goto err_put_features; return 0; err_put_features: kobject_put(features_node); err_put_instances: kobject_put(instances_node); err_put_root: kobject_put(sysfs_root); return res; } void incfs_cleanup_sysfs(void) { if (features_node) { sysfs_remove_group(features_node, &attr_group); kobject_put(features_node); } kobject_put(instances_node); kobject_put(sysfs_root); } /****************************************************************************** * Define sys/fs/incrementalfs/instances// *****************************************************************************/ #define __DECLARE_STATUS_FLAG(name) \ static ssize_t name##_show(struct kobject *kobj, \ struct kobj_attribute *attr, char *buff) \ { \ struct incfs_sysfs_node *node = container_of(kobj, \ struct incfs_sysfs_node, isn_sysfs_node); \ \ return sysfs_emit(buff, "%d\n", node->isn_mi->mi_##name); \ } \ \ static struct kobj_attribute name##_attr = __ATTR_RO(name) #define __DECLARE_STATUS_FLAG64(name) \ static ssize_t name##_show(struct kobject *kobj, \ struct kobj_attribute *attr, char *buff) \ { \ struct incfs_sysfs_node *node = container_of(kobj, \ struct incfs_sysfs_node, isn_sysfs_node); \ \ return sysfs_emit(buff, "%lld\n", node->isn_mi->mi_##name); \ } \ \ static struct kobj_attribute name##_attr = __ATTR_RO(name) __DECLARE_STATUS_FLAG(reads_failed_timed_out); __DECLARE_STATUS_FLAG(reads_failed_hash_verification); __DECLARE_STATUS_FLAG(reads_failed_other); __DECLARE_STATUS_FLAG(reads_delayed_pending); __DECLARE_STATUS_FLAG64(reads_delayed_pending_us); __DECLARE_STATUS_FLAG(reads_delayed_min); __DECLARE_STATUS_FLAG64(reads_delayed_min_us); static struct attribute *mount_attributes[] = { &reads_failed_timed_out_attr.attr, &reads_failed_hash_verification_attr.attr, &reads_failed_other_attr.attr, &reads_delayed_pending_attr.attr, &reads_delayed_pending_us_attr.attr, &reads_delayed_min_attr.attr, &reads_delayed_min_us_attr.attr, NULL, }; static void incfs_sysfs_release(struct kobject *kobj) { struct incfs_sysfs_node *node = container_of(kobj, struct incfs_sysfs_node, isn_sysfs_node); complete(&node->isn_completion); } static const struct attribute_group mount_attr_group = { .attrs = mount_attributes, }; static struct kobj_type incfs_kobj_node_ktype = { .sysfs_ops = &kobj_sysfs_ops, .release = &incfs_sysfs_release, }; struct incfs_sysfs_node *incfs_add_sysfs_node(const char *name, struct mount_info *mi) { struct incfs_sysfs_node *node = NULL; int error; if (!name) return NULL; node = kzalloc(sizeof(*node), GFP_NOFS); 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) goto err; error = sysfs_create_group(&node->isn_sysfs_node, &mount_attr_group); if (error) goto err; return node; err: /* * Note kobject_put always calls release, so incfs_sysfs_release will * free node */ kobject_put(&node->isn_sysfs_node); return ERR_PTR(error); } void incfs_free_sysfs_node(struct incfs_sysfs_node *node) { if (!node) return; 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); }