ANDROID: incremental fs: Move throttling to outside page lock
Bug: 241479010 Test: incfs_test passes, play confirm behavior in bug is fixed Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: Ie51f2b76d0873057f54fecf7fcc793c66df20969
This commit is contained in:

committed by
Treehugger Robot

parent
5d9b0e83e3
commit
7fd4fbe615
@@ -15,6 +15,12 @@ Contact: Paul Lawrence <paullawrence@google.com>
|
|||||||
Description: Reads 'supported'. Present if zstd compression is supported
|
Description: Reads 'supported'. Present if zstd compression is supported
|
||||||
for data blocks.
|
for data blocks.
|
||||||
|
|
||||||
|
What: /sys/fs/incremental-fs/features/bugfix_throttling
|
||||||
|
Date: January 2023
|
||||||
|
Contact: Paul Lawrence <paullawrence@google.com>
|
||||||
|
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]
|
What: /sys/fs/incremental-fs/instances/[name]
|
||||||
Date: April 2021
|
Date: April 2021
|
||||||
Contact: Paul Lawrence <paullawrence@google.com>
|
Contact: Paul Lawrence <paullawrence@google.com>
|
||||||
|
@@ -35,6 +35,9 @@ Features
|
|||||||
/sys/fs/incremental-fs/features/zstd
|
/sys/fs/incremental-fs/features/zstd
|
||||||
Reads 'supported'. Present if zstd compression is supported for data blocks.
|
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
|
Optional per mount
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
* Copyright 2019 Google LLC
|
* Copyright 2019 Google LLC
|
||||||
*/
|
*/
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fsverity.h>
|
#include <linux/fsverity.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
@@ -1104,25 +1103,10 @@ static void notify_pending_reads(struct mount_info *mi,
|
|||||||
wake_up_all(&mi->mi_blocks_written_notif_wq);
|
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,
|
static int wait_for_data_block(struct data_file *df, int block_index,
|
||||||
struct data_file_block *res_block,
|
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_block block = {};
|
||||||
struct data_file_segment *segment = NULL;
|
struct data_file_segment *segment = NULL;
|
||||||
@@ -1130,7 +1114,7 @@ static int wait_for_data_block(struct data_file *df, int block_index,
|
|||||||
struct mount_info *mi = NULL;
|
struct mount_info *mi = NULL;
|
||||||
int error;
|
int error;
|
||||||
int wait_res = 0;
|
int wait_res = 0;
|
||||||
unsigned int delayed_pending_us = 0, delayed_min_us = 0;
|
unsigned int delayed_pending_us = 0;
|
||||||
bool delayed_pending = false;
|
bool delayed_pending = false;
|
||||||
|
|
||||||
if (!df || !res_block)
|
if (!df || !res_block)
|
||||||
@@ -1161,8 +1145,7 @@ static int wait_for_data_block(struct data_file *df, int block_index,
|
|||||||
if (is_data_block_present(&block)) {
|
if (is_data_block_present(&block)) {
|
||||||
*res_block = block;
|
*res_block = block;
|
||||||
if (timeouts && timeouts->min_time_us) {
|
if (timeouts && timeouts->min_time_us) {
|
||||||
delayed_min_us = timeouts->min_time_us;
|
*delayed_min_us = timeouts->min_time_us;
|
||||||
error = usleep_interruptible(delayed_min_us);
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1209,13 +1192,9 @@ static int wait_for_data_block(struct data_file *df, int block_index,
|
|||||||
delayed_pending = true;
|
delayed_pending = true;
|
||||||
delayed_pending_us = timeouts->max_pending_time_us -
|
delayed_pending_us = timeouts->max_pending_time_us -
|
||||||
jiffies_to_usecs(wait_res);
|
jiffies_to_usecs(wait_res);
|
||||||
if (timeouts->min_pending_time_us > delayed_pending_us) {
|
if (timeouts->min_pending_time_us > delayed_pending_us)
|
||||||
delayed_min_us = timeouts->min_pending_time_us -
|
*delayed_min_us = timeouts->min_pending_time_us -
|
||||||
delayed_pending_us;
|
delayed_pending_us;
|
||||||
error = usleep_interruptible(delayed_min_us);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = down_read_killable(&segment->rwsem);
|
error = down_read_killable(&segment->rwsem);
|
||||||
if (error)
|
if (error)
|
||||||
@@ -1250,9 +1229,9 @@ out:
|
|||||||
delayed_pending_us;
|
delayed_pending_us;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delayed_min_us) {
|
if (delayed_min_us && *delayed_min_us) {
|
||||||
mi->mi_reads_delayed_min++;
|
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;
|
return 0;
|
||||||
@@ -1282,7 +1261,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,
|
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
||||||
int index, struct mem_range tmp,
|
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;
|
loff_t pos;
|
||||||
ssize_t result;
|
ssize_t result;
|
||||||
@@ -1301,7 +1281,8 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
|||||||
mi = df->df_mount_info;
|
mi = df->df_mount_info;
|
||||||
bfc = df->df_backing_file_context;
|
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)
|
if (result < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@@ -429,7 +429,8 @@ struct incfs_read_data_file_timeouts {
|
|||||||
|
|
||||||
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
||||||
int index, struct mem_range tmp,
|
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,
|
ssize_t incfs_read_merkle_tree_blocks(struct mem_range dst,
|
||||||
struct data_file *df, size_t offset);
|
struct data_file *df, size_t offset);
|
||||||
|
@@ -33,11 +33,13 @@ static struct kobj_attribute name##_attr = __ATTR_RO(name)
|
|||||||
DECLARE_FEATURE_FLAG(corefs);
|
DECLARE_FEATURE_FLAG(corefs);
|
||||||
DECLARE_FEATURE_FLAG(zstd);
|
DECLARE_FEATURE_FLAG(zstd);
|
||||||
DECLARE_FEATURE_FLAG(v2);
|
DECLARE_FEATURE_FLAG(v2);
|
||||||
|
DECLARE_FEATURE_FLAG(bugfix_throttling);
|
||||||
|
|
||||||
static struct attribute *attributes[] = {
|
static struct attribute *attributes[] = {
|
||||||
&corefs_attr.attr,
|
&corefs_attr.attr,
|
||||||
&zstd_attr.attr,
|
&zstd_attr.attr,
|
||||||
&v2_attr.attr,
|
&v2_attr.attr,
|
||||||
|
&bugfix_throttling_attr.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -323,7 +323,7 @@ static int incfs_build_merkle_tree(struct file *f, struct data_file *df,
|
|||||||
|
|
||||||
if (lvl == 0)
|
if (lvl == 0)
|
||||||
result = incfs_read_data_file_block(partial_buf,
|
result = incfs_read_data_file_block(partial_buf,
|
||||||
f, i, tmp, NULL);
|
f, i, tmp, NULL, NULL);
|
||||||
else {
|
else {
|
||||||
hash_level_offset = hash_offset +
|
hash_level_offset = hash_offset +
|
||||||
hash_tree->hash_level_suboffset[lvl - 1];
|
hash_tree->hash_level_suboffset[lvl - 1];
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/fs_stack.h>
|
#include <linux/fs_stack.h>
|
||||||
@@ -477,7 +478,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,
|
static int read_single_page_timeouts(struct data_file *df, struct file *f,
|
||||||
int block_index, struct mem_range range,
|
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 mount_info *mi = df->df_mount_info;
|
||||||
struct incfs_read_data_file_timeouts timeouts = {
|
struct incfs_read_data_file_timeouts timeouts = {
|
||||||
@@ -509,7 +511,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,
|
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)
|
static int read_single_page(struct file *f, struct page *page)
|
||||||
@@ -522,6 +540,7 @@ static int read_single_page(struct file *f, struct page *page)
|
|||||||
int result = 0;
|
int result = 0;
|
||||||
void *page_start;
|
void *page_start;
|
||||||
int block_index;
|
int block_index;
|
||||||
|
unsigned int delayed_min_us = 0;
|
||||||
|
|
||||||
if (!df) {
|
if (!df) {
|
||||||
SetPageError(page);
|
SetPageError(page);
|
||||||
@@ -547,7 +566,8 @@ static int read_single_page(struct file *f, struct page *page)
|
|||||||
bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE);
|
bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE);
|
||||||
|
|
||||||
read_result = read_single_page_timeouts(df, f, block_index,
|
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));
|
free_pages((unsigned long)tmp.data, get_order(tmp.len));
|
||||||
} else {
|
} else {
|
||||||
@@ -569,6 +589,8 @@ err:
|
|||||||
flush_dcache_page(page);
|
flush_dcache_page(page);
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
|
if (delayed_min_us)
|
||||||
|
usleep_interruptible(delayed_min_us);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user