diff --git a/Documentation/ABI/testing/sysfs-fs-incfs b/Documentation/ABI/testing/sysfs-fs-incfs index 690c687c67ab..e4e05f950f9e 100644 --- a/Documentation/ABI/testing/sysfs-fs-incfs +++ b/Documentation/ABI/testing/sysfs-fs-incfs @@ -15,6 +15,12 @@ Contact: Paul Lawrence Description: Reads 'supported'. Present if zstd compression is supported for data blocks. +What: /sys/fs/incremental-fs/features/bugfix_throttling +Date: January 2023 +Contact: Paul Lawrence +Description: Reads 'supported'. Present if the throttling lock bug is fixed + https://android-review.git.corp.google.com/c/kernel/common/+/2381827 + What: /sys/fs/incremental-fs/instances/[name] Date: April 2021 Contact: Paul Lawrence diff --git a/Documentation/filesystems/incfs.rst b/Documentation/filesystems/incfs.rst index 19db303b8327..f0fb1d02d530 100644 --- a/Documentation/filesystems/incfs.rst +++ b/Documentation/filesystems/incfs.rst @@ -35,6 +35,9 @@ Features /sys/fs/incremental-fs/features/zstd Reads 'supported'. Present if zstd compression is supported for data blocks. +/sys/fs/incremental-fs/features/bugfix_throttling + Reads 'supported'. Present if the throttling lock bug is fixed + Optional per mount ------------------ diff --git a/fs/incfs/data_mgmt.c b/fs/incfs/data_mgmt.c index 5dbfe93d65e9..5abb20a1098c 100644 --- a/fs/incfs/data_mgmt.c +++ b/fs/incfs/data_mgmt.c @@ -3,7 +3,6 @@ * Copyright 2019 Google LLC */ #include -#include #include #include #include @@ -1106,25 +1105,10 @@ static void notify_pending_reads(struct mount_info *mi, wake_up_all(&mi->mi_blocks_written_notif_wq); } -static int usleep_interruptible(u32 us) -{ - /* See: - * https://www.kernel.org/doc/Documentation/timers/timers-howto.txt - * for explanation - */ - if (us < 10) { - udelay(us); - return 0; - } else if (us < 20000) { - usleep_range(us, us + us / 10); - return 0; - } else - return msleep_interruptible(us / 1000); -} - static int wait_for_data_block(struct data_file *df, int block_index, struct data_file_block *res_block, - struct incfs_read_data_file_timeouts *timeouts) + struct incfs_read_data_file_timeouts *timeouts, + unsigned int *delayed_min_us) { struct data_file_block block = {}; struct data_file_segment *segment = NULL; @@ -1132,7 +1116,7 @@ static int wait_for_data_block(struct data_file *df, int block_index, struct mount_info *mi = NULL; int error; int wait_res = 0; - unsigned int delayed_pending_us = 0, delayed_min_us = 0; + unsigned int delayed_pending_us = 0; bool delayed_pending = false; if (!df || !res_block) @@ -1163,8 +1147,7 @@ static int wait_for_data_block(struct data_file *df, int block_index, if (is_data_block_present(&block)) { *res_block = block; if (timeouts && timeouts->min_time_us) { - delayed_min_us = timeouts->min_time_us; - error = usleep_interruptible(delayed_min_us); + *delayed_min_us = timeouts->min_time_us; goto out; } return 0; @@ -1211,13 +1194,9 @@ static int wait_for_data_block(struct data_file *df, int block_index, delayed_pending = true; 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 - + 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) @@ -1252,9 +1231,9 @@ out: delayed_pending_us; } - if (delayed_min_us) { + if (delayed_min_us && *delayed_min_us) { mi->mi_reads_delayed_min++; - mi->mi_reads_delayed_min_us += delayed_min_us; + mi->mi_reads_delayed_min_us += *delayed_min_us; } return 0; @@ -1284,7 +1263,8 @@ static int incfs_update_sysfs_error(struct file *file, int index, int result, ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f, int index, struct mem_range tmp, - struct incfs_read_data_file_timeouts *timeouts) + struct incfs_read_data_file_timeouts *timeouts, + unsigned int *delayed_min_us) { loff_t pos; ssize_t result; @@ -1303,7 +1283,8 @@ 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, &block, timeouts); + result = wait_for_data_block(df, index, &block, timeouts, + delayed_min_us); if (result < 0) goto out; diff --git a/fs/incfs/data_mgmt.h b/fs/incfs/data_mgmt.h index 2b6121d6131f..8bd5c2fcf6dc 100644 --- a/fs/incfs/data_mgmt.h +++ b/fs/incfs/data_mgmt.h @@ -429,7 +429,8 @@ struct incfs_read_data_file_timeouts { ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f, int index, struct mem_range tmp, - struct incfs_read_data_file_timeouts *timeouts); + struct incfs_read_data_file_timeouts *timeouts, + unsigned int *delayed_min_us); ssize_t incfs_read_merkle_tree_blocks(struct mem_range dst, struct data_file *df, size_t offset); diff --git a/fs/incfs/sysfs.c b/fs/incfs/sysfs.c index 360f03ca9b63..5c7e0fd4496c 100644 --- a/fs/incfs/sysfs.c +++ b/fs/incfs/sysfs.c @@ -33,11 +33,13 @@ 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); static struct attribute *attributes[] = { &corefs_attr.attr, &zstd_attr.attr, &v2_attr.attr, + &bugfix_throttling_attr.attr, NULL, }; diff --git a/fs/incfs/verity.c b/fs/incfs/verity.c index b69656b9e845..562a8e774178 100644 --- a/fs/incfs/verity.c +++ b/fs/incfs/verity.c @@ -323,7 +323,7 @@ static int incfs_build_merkle_tree(struct file *f, struct data_file *df, if (lvl == 0) result = incfs_read_data_file_block(partial_buf, - f, i, tmp, NULL); + f, i, tmp, NULL, NULL); else { hash_level_offset = hash_offset + hash_tree->hash_level_suboffset[lvl - 1]; diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index 4ac6e5e7851e..61d62167ad2e 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -483,7 +484,8 @@ static struct dentry *open_or_create_special_dir(struct dentry *backing_dir, static int read_single_page_timeouts(struct data_file *df, struct file *f, int block_index, struct mem_range range, - struct mem_range tmp) + struct mem_range tmp, + unsigned int *delayed_min_us) { struct mount_info *mi = df->df_mount_info; struct incfs_read_data_file_timeouts timeouts = { @@ -515,7 +517,23 @@ static int read_single_page_timeouts(struct data_file *df, struct file *f, } return incfs_read_data_file_block(range, f, block_index, tmp, - &timeouts); + &timeouts, delayed_min_us); +} + +static int usleep_interruptible(u32 us) +{ + /* See: + * https://www.kernel.org/doc/Documentation/timers/timers-howto.txt + * for explanation + */ + if (us < 10) { + udelay(us); + return 0; + } else if (us < 20000) { + usleep_range(us, us + us / 10); + return 0; + } else + return msleep_interruptible(us / 1000); } static int read_single_page(struct file *f, struct page *page) @@ -528,6 +546,7 @@ static int read_single_page(struct file *f, struct page *page) int result = 0; void *page_start; int block_index; + unsigned int delayed_min_us = 0; if (!df) { SetPageError(page); @@ -553,7 +572,8 @@ static int read_single_page(struct file *f, struct page *page) bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE); read_result = read_single_page_timeouts(df, f, block_index, - range(page_start, bytes_to_read), tmp); + range(page_start, bytes_to_read), tmp, + &delayed_min_us); free_pages((unsigned long)tmp.data, get_order(tmp.len)); } else { @@ -575,6 +595,8 @@ err: flush_dcache_page(page); kunmap(page); unlock_page(page); + if (delayed_min_us) + usleep_interruptible(delayed_min_us); return result; }