In order to not freeze on corrupt data, we need to turn off FAULT_FLAG_ALLOW_RETRY. However, this means we no longer retry on EINTR, so an interrupted read will lead to page faults. The fault handler does not seem to allow dynamic decisions as to whether to turn on or off this flag. To resolve both issues, add a flag to indicate if there are corrupt pages in a file, and only if there are turn off this flag. Also fsanitize changed the behavior of mlock - mlock should fail if the page reads fail, but with fsanitize it returns 0 then page faults on access. This broke this test, and fsanitize offers little value on test code, so disable it. Test: incfs_test passes Bug: 343532239 Change-Id: Id2ced4be3310109206d65dcc92dea05c05131182 Signed-off-by: Paul Lawrence <paullawrence@google.com>
208 lines
5.3 KiB
C
208 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2021 Google LLC
|
|
*/
|
|
#include <linux/fs.h>
|
|
#include <linux/kobject.h>
|
|
|
|
#include <uapi/linux/incrementalfs.h>
|
|
|
|
#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);
|
|
DECLARE_FEATURE_FLAG(bugfix_throttling);
|
|
DECLARE_FEATURE_FLAG(bugfix_inode_eviction);
|
|
DECLARE_FEATURE_FLAG(bugfix_retry_page_fault);
|
|
|
|
static struct attribute *attributes[] = {
|
|
&corefs_attr.attr,
|
|
&zstd_attr.attr,
|
|
&v2_attr.attr,
|
|
&bugfix_throttling_attr.attr,
|
|
&bugfix_inode_eviction_attr.attr,
|
|
&bugfix_retry_page_fault_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/<name>/
|
|
*****************************************************************************/
|
|
#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);
|
|
}
|