diff mbox series

[3/5] samples/kernfs: Add counter file to each directory

Message ID 20250121154659.43079-1-me@davidreaver.com (mailing list archive)
State New
Headers show
Series samples/kernfs: Add a pseudo-filesystem to demonstrate kernfs usage | expand

Commit Message

David Reaver Jan. 21, 2025, 3:46 p.m. UTC
The counter file is automatically added to all sample_kernfs
directories (including the root directory). This demonstrates how to tie an
internal datastructure -- sample_kernfs_directory in this case -- to kernfs
nodes via kernfs_node->priv. Also demonstrates how to read and write simple
integer values to/from kernfs files.

Signed-off-by: David Reaver <me@davidreaver.com>
---
 samples/kernfs/sample_kernfs.c | 110 ++++++++++++++++++++++++++++++++-
 1 file changed, 108 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/samples/kernfs/sample_kernfs.c b/samples/kernfs/sample_kernfs.c
index 3ea8411a72ae..b6d44fc3b935 100644
--- a/samples/kernfs/sample_kernfs.c
+++ b/samples/kernfs/sample_kernfs.c
@@ -14,6 +14,93 @@ 

 #define SAMPLE_KERNFS_MAGIC 0x8d000ff0

+/**
+ * struct sample_kernfs_directory - Represents a directory in the pseudo-filesystem
+ * @count: Holds the current count in the counter file.
+ */
+struct sample_kernfs_directory {
+	atomic64_t count;
+};
+
+static struct sample_kernfs_directory *sample_kernfs_create_dir(void)
+{
+	struct sample_kernfs_directory *dir;
+
+	dir = kzalloc(sizeof(struct sample_kernfs_directory), GFP_KERNEL);
+	if (!dir)
+		return NULL;
+
+	return dir;
+}
+
+static struct sample_kernfs_directory *kernfs_of_to_dir(struct kernfs_open_file *of)
+{
+	struct kernfs_node *dir_kn = kernfs_get_parent(of->kn);
+	struct sample_kernfs_directory *dir = dir_kn->priv;
+
+	/* kernfs_get_parent adds a reference; drop it with kernfs_put */
+	kernfs_put(dir_kn);
+
+	return dir;
+}
+
+static int sample_kernfs_counter_seq_show(struct seq_file *sf, void *v)
+{
+	struct kernfs_open_file *of = sf->private;
+	struct sample_kernfs_directory *counter_dir = kernfs_of_to_dir(of);
+	u64 count = atomic64_inc_return(&counter_dir->count);
+
+	seq_printf(sf, "%llu\n", count);
+
+	return 0;
+}
+
+static ssize_t sample_kernfs_counter_write(struct kernfs_open_file *of, char *buf,
+					   size_t nbytes, loff_t off)
+{
+	struct sample_kernfs_directory *counter_dir = kernfs_of_to_dir(of);
+	int ret;
+	u64 new_value;
+
+	ret = kstrtou64(strstrip(buf), 10, &new_value);
+	if (ret)
+		return ret;
+
+	atomic64_set(&counter_dir->count, new_value);
+
+	return nbytes;
+}
+
+static struct kernfs_ops counter_kf_ops = {
+	.seq_show	= sample_kernfs_counter_seq_show,
+	.write		= sample_kernfs_counter_write,
+};
+
+static int sample_kernfs_add_file(struct kernfs_node *dir_kn, const char *name,
+				  struct kernfs_ops *ops)
+{
+	struct kernfs_node *kn;
+
+	kn = __kernfs_create_file(dir_kn, name, 0666, current_fsuid(),
+				  current_fsgid(), 0, ops, NULL, NULL, NULL);
+
+	if (IS_ERR(kn))
+		return PTR_ERR(kn);
+
+	return 0;
+}
+
+static int sample_kernfs_populate_dir(struct kernfs_node *dir_kn)
+{
+	int err;
+
+	err = sample_kernfs_add_file(dir_kn, "counter", &counter_kf_ops);
+	if (err)
+		return err;
+
+	return 0;
+}
+
 static void sample_kernfs_fs_context_free(struct fs_context *fc)
 {
 	struct kernfs_fs_context *kfc = fc->fs_private;
@@ -30,6 +117,7 @@  static const struct fs_context_operations sample_kernfs_fs_context_ops = {
 static int sample_kernfs_init_fs_context(struct fs_context *fc)
 {
 	struct kernfs_fs_context *kfc;
+	struct sample_kernfs_directory *root_dir;
 	struct kernfs_root *root;
 	int err;

@@ -37,10 +125,17 @@  static int sample_kernfs_init_fs_context(struct fs_context *fc)
 	if (!kfc)
 		return -ENOMEM;

-	root = kernfs_create_root(NULL, 0, NULL);
+	root_dir = sample_kernfs_create_dir();
+	if (!root_dir) {
+		err = -ENOMEM;
+		goto err_free_kfc;
+	}
+
+	/* dir gets stored in root->priv so we can access it later. */
+	root = kernfs_create_root(NULL, 0, root_dir);
 	if (IS_ERR(root)) {
 		err = PTR_ERR(root);
-		goto err_free_kfc;
+		goto err_free_dir;
 	}

 	kfc->root = root;
@@ -49,8 +144,16 @@  static int sample_kernfs_init_fs_context(struct fs_context *fc)
 	fc->ops = &sample_kernfs_fs_context_ops;
 	fc->global = true;

+	err = sample_kernfs_populate_dir(kernfs_root_to_node(root));
+	if (err)
+		goto err_free_root;
+
 	return 0;

+err_free_root:
+	kernfs_destroy_root(root);
+err_free_dir:
+	kfree(root_dir);
 err_free_kfc:
 	kfree(kfc);
 	return err;
@@ -59,9 +162,12 @@  static int sample_kernfs_init_fs_context(struct fs_context *fc)
 static void sample_kernfs_kill_sb(struct super_block *sb)
 {
 	struct kernfs_root *root = kernfs_root_from_sb(sb);
+	struct kernfs_node *root_kn = kernfs_root_to_node(root);
+	struct sample_kernfs_directory *root_dir = root_kn->priv;

 	kernfs_kill_sb(sb);
 	kernfs_destroy_root(root);
+	kfree(root_dir);
 }

 static struct file_system_type sample_kernfs_fs_type = {