Files
scoutfs/kmod/src/sysfs.c
Auke Kok 8885486bc8 Add several low level includes.
Newer kernels include less header dependencies by default, so we have
to add these.

Signed-off-by: Auke Kok <auke.kok@versity.com>
2024-10-03 12:41:05 -07:00

276 lines
6.5 KiB
C

/*
* Copyright (C) 2017 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 <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include "super.h"
#include "sysfs.h"
static struct kset *scoutfs_kset;
struct sysfs_info {
struct super_block *sb;
struct kobject sb_id_kobj;
struct completion sb_id_comp;
};
#define KOBJ_TO_SB(kobj, which) \
container_of(kobj, struct sysfs_info, which)->sb
struct attr_funcs {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
char *buf);
};
#define ATTR_FUNCS_RO(_name) \
static struct attr_funcs _name##_attr_funcs = __ATTR_RO(_name)
static ssize_t data_device_maj_min_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct super_block *sb = KOBJ_TO_SB(kobj, sb_id_kobj);
return snprintf(buf, PAGE_SIZE, "%u:%u\n",
MAJOR(sb->s_bdev->bd_dev), MINOR(sb->s_bdev->bd_dev));
}
ATTR_FUNCS_RO(data_device_maj_min);
static ssize_t format_version_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct super_block *sb = KOBJ_TO_SB(kobj, sb_id_kobj);
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
return snprintf(buf, PAGE_SIZE, "%llu\n", sbi->fmt_vers);
}
ATTR_FUNCS_RO(format_version);
static ssize_t fsid_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct super_block *sb = KOBJ_TO_SB(kobj, sb_id_kobj);
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
return snprintf(buf, PAGE_SIZE, "%016llx\n", sbi->fsid);
}
ATTR_FUNCS_RO(fsid);
static ssize_t rid_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct super_block *sb = KOBJ_TO_SB(kobj, sb_id_kobj);
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
return snprintf(buf, PAGE_SIZE, "%016llx\n", sbi->rid);
}
ATTR_FUNCS_RO(rid);
/*
* ops are defined per type, not per attribute. To have attributes with
* different types that want different funcs we wrap them with a struct
* that has per-type funcs.
*/
static ssize_t attr_funcs_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct attr_funcs *af = container_of(attr, struct attr_funcs, attr);
return af->show(kobj, attr, buf);
}
#define KTYPE(_name) \
static void _name##_release(struct kobject *kobj) \
{ \
struct sysfs_info *sfsinfo; \
\
sfsinfo = container_of(kobj, struct sysfs_info, _name##_kobj);\
\
complete(&sfsinfo->_name##_comp); \
} \
static const struct sysfs_ops _name##_sysfs_ops = { \
.show = attr_funcs_show, \
}; \
\
static struct kobj_type _name##_ktype = { \
.default_attrs = _name##_attrs, \
.sysfs_ops = &_name##_sysfs_ops, \
.release = _name##_release, \
};
static struct attribute *sb_id_attrs[] = {
&data_device_maj_min_attr_funcs.attr,
&format_version_attr_funcs.attr,
&fsid_attr_funcs.attr,
&rid_attr_funcs.attr,
NULL,
};
KTYPE(sb_id);
struct kobject *scoutfs_sysfs_sb_dir(struct super_block *sb)
{
struct sysfs_info *sfsinfo = SCOUTFS_SB(sb)->sfsinfo;
return &sfsinfo->sb_id_kobj;
}
static void kobj_del_put_wait(struct kobject *kobj, struct completion *comp)
{
kobject_del(kobj);
kobject_put(kobj);
wait_for_completion(comp);
}
#define shutdown_kobj(sfinfo, _name) \
kobj_del_put_wait(&sfsinfo->_name##_kobj, &sfsinfo->_name##_comp)
static void scoutfs_sysfs_release(struct kobject *kobj)
{
DECLARE_SCOUTFS_SYSFS_ATTRS(ssa, kobj);
complete(&ssa->comp);
}
void scoutfs_sysfs_init_attrs(struct super_block *sb,
struct scoutfs_sysfs_attrs *ssa)
{
ssa->name = NULL;
}
/*
* If this returns success then the file will be visible and show can
* be called until unmount.
*/
int scoutfs_sysfs_create_attrs_parent(struct super_block *sb,
struct kobject *parent,
struct scoutfs_sysfs_attrs *ssa,
struct attribute **attrs, char *fmt, ...)
{
va_list args;
size_t name_len;
size_t size;
int ret;
/* ssa should have seen init or destroy */
if (WARN_ON_ONCE(ssa->name != NULL))
return -EINVAL;
ssa->sb = sb;
init_completion(&ssa->comp);
ssa->ktype.default_attrs = attrs;
ssa->ktype.sysfs_ops = &kobj_sysfs_ops;
ssa->ktype.release = scoutfs_sysfs_release;
va_start(args, fmt);
name_len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (WARN_ON_ONCE(name_len < 1 || name_len > NAME_MAX)) {
ret = -EINVAL;
goto out;
}
size = name_len + 1; /* with null */
ssa->name = kmalloc(size, GFP_KERNEL);
if (!ssa->name) {
ret = -ENOMEM;
goto out;
}
va_start(args, fmt);
ret = vsnprintf(ssa->name, size, fmt, args);
va_end(args);
if (ret != name_len) {
ret = -EINVAL;
goto out;
}
ret = kobject_init_and_add(&ssa->kobj, &ssa->ktype, parent,
"%s", ssa->name);
out:
if (ret) {
kfree(ssa->name);
ssa->name = NULL;
}
return ret;
}
void scoutfs_sysfs_destroy_attrs(struct super_block *sb,
struct scoutfs_sysfs_attrs *ssa)
{
if (ssa->name) {
kobject_del(&ssa->kobj);
kobject_put(&ssa->kobj);
wait_for_completion(&ssa->comp);
kfree(ssa->name);
ssa->name = NULL;
}
}
/*
* Only the return from kobj_init_and_add() tells us if the kobj needs
* to be cleaned up or not. This must manually clean up the kobjs and
* only leave full cleanup to _destroy_.
*/
int scoutfs_setup_sysfs(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct sysfs_info *sfsinfo;
int ret;
sfsinfo = kzalloc(sizeof(struct sysfs_info), GFP_KERNEL);
if (!sfsinfo)
return -ENOMEM;
sfsinfo->sb = sb;
sbi->sfsinfo = sfsinfo;
init_completion(&sfsinfo->sb_id_comp);
ret = kobject_init_and_add(&sfsinfo->sb_id_kobj, &sb_id_ktype,
&scoutfs_kset->kobj, SCSBF, SCSB_ARGS(sb));
if (ret)
kfree(sfsinfo);
return ret;
}
void scoutfs_destroy_sysfs(struct super_block *sb)
{
struct scoutfs_sb_info *sbi = SCOUTFS_SB(sb);
struct sysfs_info *sfsinfo = sbi->sfsinfo;
if (sfsinfo) {
shutdown_kobj(sfsinfo, sb_id);
kfree(sfsinfo);
sbi->sfsinfo = NULL;
}
}
int __init scoutfs_sysfs_init(void)
{
scoutfs_kset = kset_create_and_add("scoutfs", NULL, fs_kobj);
if (!scoutfs_kset)
return -ENOMEM;
return 0;
}
void scoutfs_sysfs_exit(void)
{
if (scoutfs_kset)
kset_unregister(scoutfs_kset);
}