Skip to content

Commit

Permalink
ANDROID: Incremental fs: Add status to sysfs
Browse files Browse the repository at this point in the history
Adding seven sysfs entries per mount:

reads_failed_timed_out
reads_failed_hash_verification
reads_failed_other
reads_delayed_pending
reads_delayed_pending_us
reads_delayed_min
reads_delayed_min_us

to allow for status monitoring from userland

Change-Id: I50677511c2af4778ba0c574bb80323f31425b4d0
Test: incfs_test passes
Bug: 160634343
Bug: 184291759
Signed-off-by: Paul Lawrence <paullawrence@google.com>
  • Loading branch information
PaulLawrenceGoogle committed Apr 23, 2021
1 parent 3d471f0 commit 44ffa65
Show file tree
Hide file tree
Showing 11 changed files with 700 additions and 122 deletions.
64 changes: 64 additions & 0 deletions Documentation/ABI/testing/sysfs-fs-incfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
What: /sys/fs/incremental-fs/features/corefs
Date: 2019
Contact: Paul Lawrence <paullawrence@google.com>
Description: Reads 'supported'. Always present.

What: /sys/fs/incremental-fs/features/v2
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Reads 'supported'. Present if all v2 features of incfs are
supported.

What: /sys/fs/incremental-fs/features/zstd
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Reads 'supported'. Present if zstd compression is supported
for data blocks.

What: /sys/fs/incremental-fs/instances/[name]
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Folder created when incfs is mounted with the sysfs_name=[name]
option. If this option is used, the following values are created
in this folder.

What: /sys/fs/incremental-fs/instances/[name]/reads_delayed_min
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns a count of the number of reads that were delayed as a
result of the per UID read timeouts min time setting.

What: /sys/fs/incremental-fs/instances/[name]/reads_delayed_min_us
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns total delay time for all files since first mount as a
result of the per UID read timeouts min time setting.

What: /sys/fs/incremental-fs/instances/[name]/reads_delayed_pending
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns a count of the number of reads that were delayed as a
result of waiting for a pending read.

What: /sys/fs/incremental-fs/instances/[name]/reads_delayed_pending_us
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns total delay time for all files since first mount as a
result of waiting for a pending read.

What: /sys/fs/incremental-fs/instances/[name]/reads_failed_hash_verification
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns number of reads that failed because of hash verification
failures.

What: /sys/fs/incremental-fs/instances/[name]/reads_failed_other
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns number of reads that failed for reasons other than
timing out or hash failures.

What: /sys/fs/incremental-fs/instances/[name]/reads_failed_timed_out
Date: April 2021
Contact: Paul Lawrence <paullawrence@google.com>
Description: Returns number of reads that timed out.
82 changes: 82 additions & 0 deletions Documentation/filesystems/incfs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.. SPDX-License-Identifier: GPL-2.0
=================================================
incfs: A stacked incremental filesystem for Linux
=================================================

/sys/fs interface
=================

Please update Documentation/ABI/testing/sys-fs-incfs if you update this
section.

incfs creates the following files in /sys/fs.

Features
--------

/sys/fs/incremental-fs/features/corefs
Reads 'supported'. Always present.

/sys/fs/incremental-fs/features/v2
Reads 'supported'. Present if all v2 features of incfs are supported. These
are:
fs-verity support
inotify support
ioclts:
INCFS_IOC_SET_READ_TIMEOUTS
INCFS_IOC_GET_READ_TIMEOUTS
INCFS_IOC_GET_BLOCK_COUNT
INCFS_IOC_CREATE_MAPPED_FILE
.incomplete folder
.blocks_written pseudo file
report_uid mount option

/sys/fs/incremental-fs/features/zstd
Reads 'supported'. Present if zstd compression is supported for data blocks.

Optional per mount
------------------

For each incfs mount, the mount option sysfs_name=[name] creates a /sys/fs
node called:

/sys/fs/incremental-fs/instances/[name]

This will contain the following files:

/sys/fs/incremental-fs/instances/[name]/reads_delayed_min
Returns a count of the number of reads that were delayed as a result of the
per UID read timeouts min time setting.

/sys/fs/incremental-fs/instances/[name]/reads_delayed_min_us
Returns total delay time for all files since first mount as a result of the
per UID read timeouts min time setting.

/sys/fs/incremental-fs/instances/[name]/reads_delayed_pending
Returns a count of the number of reads that were delayed as a result of
waiting for a pending read.

/sys/fs/incremental-fs/instances/[name]/reads_delayed_pending_us
Returns total delay time for all files since first mount as a result of
waiting for a pending read.

/sys/fs/incremental-fs/instances/[name]/reads_failed_hash_verification
Returns number of reads that failed because of hash verification failures.

/sys/fs/incremental-fs/instances/[name]/reads_failed_other
Returns number of reads that failed for reasons other than timing out or
hash failures.

/sys/fs/incremental-fs/instances/[name]/reads_failed_timed_out
Returns number of reads that timed out.

For reads_delayed_*** settings, note that a file can count for both
reads_delayed_min and reads_delayed_pending if incfs first waits for a pending
read then has to wait further for the min time. In that case, the time spent
waiting is split between reads_delayed_pending_us, which is increased by the
time spent waiting for the pending read, and reads_delayed_min_us, which is
increased by the remainder of the time spent waiting.

Reads that timed out are not added to the reads_delayed_pending or the
reads_delayed_pending_us counters.
1 change: 1 addition & 0 deletions fs/incfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ incrementalfs-y := \
integrity.o \
main.o \
pseudo_files.o \
sysfs.o \
vfs.o

incrementalfs-$(CONFIG_FS_VERITY) += verity.o
110 changes: 80 additions & 30 deletions fs/incfs/data_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/file.h>
#include <linux/fsverity.h>
#include <linux/gfp.h>
#include <linux/kobject.h>
#include <linux/ktime.h>
#include <linux/lz4.h>
#include <linux/mm.h>
Expand All @@ -19,6 +20,7 @@
#include "data_mgmt.h"
#include "format.h"
#include "integrity.h"
#include "sysfs.h"
#include "verity.h"

static int incfs_scan_metadata_chain(struct data_file *df);
Expand Down Expand Up @@ -49,6 +51,7 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb,
{
struct mount_info *mi = NULL;
int error = 0;
struct incfs_sysfs_node *node;

mi = kzalloc(sizeof(*mi), GFP_NOFS);
if (!mi)
Expand All @@ -71,6 +74,13 @@ struct mount_info *incfs_alloc_mount_info(struct super_block *sb,
mutex_init(&mi->mi_zstd_workspace_mutex);
INIT_DELAYED_WORK(&mi->mi_zstd_cleanup_work, zstd_free_workspace);

node = incfs_add_sysfs_node(options->sysfs_name);
if (IS_ERR(node)) {
error = PTR_ERR(node);
goto err;
}
mi->mi_sysfs_node = node;

error = incfs_realloc_mount_info(mi, options);
if (error)
goto err;
Expand Down Expand Up @@ -119,6 +129,15 @@ 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 &&
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;
}

mi->mi_options = *options;
return 0;
}
Expand All @@ -142,6 +161,7 @@ void incfs_free_mount_info(struct mount_info *mi)
for (i = 0; i < ARRAY_SIZE(mi->pseudo_file_xattr); ++i)
kfree(mi->pseudo_file_xattr[i].data);
kfree(mi->mi_per_uid_read_timeouts);
incfs_free_sysfs_node(mi->mi_sysfs_node);
kfree(mi);
}

Expand Down Expand Up @@ -1088,17 +1108,16 @@ static int usleep_interruptible(u32 us)
}

static int wait_for_data_block(struct data_file *df, int block_index,
u32 min_time_us, u32 min_pending_time_us,
u32 max_pending_time_us,
struct data_file_block *res_block)
struct data_file_block *res_block,
struct incfs_read_data_file_timeouts *timeouts)
{
struct data_file_block block = {};
struct data_file_segment *segment = NULL;
struct pending_read *read = NULL;
struct mount_info *mi = NULL;
int error = 0;
int error;
int wait_res = 0;
u64 time;
unsigned int delayed_pending_us = 0, delayed_min_us = 0;

if (!df || !res_block)
return -EFAULT;
Expand Down Expand Up @@ -1126,13 +1145,16 @@ static int wait_for_data_block(struct data_file *df, int block_index,

/* If the block was found, just return it. No need to wait. */
if (is_data_block_present(&block)) {
if (min_time_us)
error = usleep_interruptible(min_time_us);
*res_block = block;
return error;
if (timeouts && timeouts->min_time_us) {
delayed_min_us = timeouts->min_time_us;
error = usleep_interruptible(delayed_min_us);
goto out;
}
return 0;
} else {
/* If it's not found, create a pending read */
if (max_pending_time_us != 0) {
if (timeouts && timeouts->max_pending_time_us) {
read = add_pending_read(df, block_index);
if (!read)
return -ENOMEM;
Expand All @@ -1142,14 +1164,17 @@ static int wait_for_data_block(struct data_file *df, int block_index,
}
}

if (min_pending_time_us)
time = ktime_get_ns();
/* Rest of function only applies if timeouts != NULL */
if (!timeouts) {
pr_warn("incfs: timeouts unexpectedly NULL\n");
return -EFSCORRUPTED;
}

/* Wait for notifications about block's arrival */
wait_res =
wait_event_interruptible_timeout(segment->new_data_arrival_wq,
(is_read_done(read)),
usecs_to_jiffies(max_pending_time_us));
(is_read_done(read)),
usecs_to_jiffies(timeouts->max_pending_time_us));

/* Woke up, the pending read is no longer needed. */
remove_pending_read(df, read);
Expand All @@ -1167,22 +1192,22 @@ static int wait_for_data_block(struct data_file *df, int block_index,
return wait_res;
}

if (min_pending_time_us) {
time = div_u64(ktime_get_ns() - time, 1000);
if (min_pending_time_us > time) {
error = usleep_interruptible(
min_pending_time_us - time);
if (error)
return error;
}
delayed_pending_us = timeouts->max_pending_time_us -
jiffies_to_usecs(wait_res);
if (timeouts->min_pending_time_us > delayed_pending_us) {
delayed_min_us = timeouts->min_pending_time_us -
delayed_pending_us;
error = usleep_interruptible(delayed_min_us);
if (error)
return error;
}

error = down_read_killable(&segment->rwsem);
if (error)
return error;

/*
* Re-read block's info now, it has just arrived and
* Re-read blocks info now, it has just arrived and
* should be available.
*/
error = get_data_file_block(df, block_index, &block);
Expand All @@ -1191,22 +1216,39 @@ static int wait_for_data_block(struct data_file *df, int block_index,
*res_block = block;
else {
/*
* Somehow wait finished successfully bug block still
* Somehow wait finished successfully but block still
* can't be found. It's not normal.
*/
pr_warn("incfs: Wait succeeded but block not found.\n");
error = -ENODATA;
}
}

up_read(&segment->rwsem);
return error;

out:
if (error)
return error;

if (!mi->mi_sysfs_node)
return 0;

if (delayed_pending_us) {
mi->mi_sysfs_node->isn_reads_delayed_pending++;
mi->mi_sysfs_node->isn_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;
}

return 0;
}

ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
int index, u32 min_time_us,
u32 min_pending_time_us, u32 max_pending_time_us,
struct mem_range tmp)
int index, struct mem_range tmp,
struct incfs_read_data_file_timeouts *timeouts)
{
loff_t pos;
ssize_t result;
Expand All @@ -1225,8 +1267,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
mi = df->df_mount_info;
bfc = df->df_backing_file_context;

result = wait_for_data_block(df, index, min_time_us,
min_pending_time_us, max_pending_time_us, &block);
result = wait_for_data_block(df, index, &block, timeouts);
if (result < 0)
goto out;

Expand Down Expand Up @@ -1269,6 +1310,15 @@ 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++;
}

return result;
}

Expand Down

0 comments on commit 44ffa65

Please sign in to comment.