ANDROID: incremental fs: Fix race between truncate and write last block
Also fix race whereby multiple providers writinig the same block would
actually write out the same block.
Note that multiple_providers_test started failing when incfs was ported
to 5.15, and these fixes are needed to make the test reliable
Bug: 264703896
Test: incfs-test passes, specifically multiple_providers_test. Ran 100
times
Change-Id: I05ad5b2b2f62cf218256222cecb79bbe9953bd97
Signed-off-by: Paul Lawrence <paullawrence@google.com>
This commit is contained in:
committed by
Treehugger Robot
parent
6a8037d4eb
commit
5d9b0e83e3
@@ -1379,7 +1379,8 @@ ssize_t incfs_read_merkle_tree_blocks(struct mem_range dst,
|
||||
}
|
||||
|
||||
int incfs_process_new_data_block(struct data_file *df,
|
||||
struct incfs_fill_block *block, u8 *data)
|
||||
struct incfs_fill_block *block, u8 *data,
|
||||
bool *complete)
|
||||
{
|
||||
struct mount_info *mi = NULL;
|
||||
struct backing_file_context *bfc = NULL;
|
||||
@@ -1418,27 +1419,42 @@ int incfs_process_new_data_block(struct data_file *df,
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
if (is_data_block_present(&existing_block)) {
|
||||
if (is_data_block_present(&existing_block))
|
||||
/* Block is already present, nothing to do here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
error = down_write_killable(&segment->rwsem);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = mutex_lock_interruptible(&bfc->bc_mutex);
|
||||
if (!error) {
|
||||
error = incfs_write_data_block_to_backing_file(
|
||||
bfc, range(data, block->data_len), block->block_index,
|
||||
df->df_blockmap_off, flags);
|
||||
mutex_unlock(&bfc->bc_mutex);
|
||||
}
|
||||
if (!error) {
|
||||
notify_pending_reads(mi, segment, block->block_index);
|
||||
atomic_inc(&df->df_data_blocks_written);
|
||||
}
|
||||
/* Recheck inside write lock */
|
||||
error = get_data_file_block(df, block->block_index, &existing_block);
|
||||
if (error)
|
||||
goto out_up_write;
|
||||
|
||||
if (is_data_block_present(&existing_block))
|
||||
goto out_up_write;
|
||||
|
||||
error = mutex_lock_interruptible(&bfc->bc_mutex);
|
||||
if (error)
|
||||
goto out_up_write;
|
||||
|
||||
error = incfs_write_data_block_to_backing_file(bfc,
|
||||
range(data, block->data_len), block->block_index,
|
||||
df->df_blockmap_off, flags);
|
||||
if (error)
|
||||
goto out_mutex_unlock;
|
||||
|
||||
if (atomic_inc_return(&df->df_data_blocks_written)
|
||||
>= df->df_data_block_count)
|
||||
*complete = true;
|
||||
|
||||
out_mutex_unlock:
|
||||
mutex_unlock(&bfc->bc_mutex);
|
||||
if (!error)
|
||||
notify_pending_reads(mi, segment, block->block_index);
|
||||
|
||||
out_up_write:
|
||||
up_write(&segment->rwsem);
|
||||
|
||||
if (error)
|
||||
|
||||
@@ -441,7 +441,8 @@ int incfs_get_filled_blocks(struct data_file *df,
|
||||
int incfs_read_file_signature(struct data_file *df, struct mem_range dst);
|
||||
|
||||
int incfs_process_new_data_block(struct data_file *df,
|
||||
struct incfs_fill_block *block, u8 *data);
|
||||
struct incfs_fill_block *block, u8 *data,
|
||||
bool *complete);
|
||||
|
||||
int incfs_process_new_hash_block(struct data_file *df,
|
||||
struct incfs_fill_block *block, u8 *data);
|
||||
|
||||
@@ -662,8 +662,7 @@ out:
|
||||
dput(file);
|
||||
}
|
||||
|
||||
static void maybe_delete_incomplete_file(struct file *f,
|
||||
struct data_file *df)
|
||||
static void handle_file_completed(struct file *f, struct data_file *df)
|
||||
{
|
||||
struct backing_file_context *bfc;
|
||||
struct mount_info *mi = df->df_mount_info;
|
||||
@@ -672,9 +671,6 @@ static void maybe_delete_incomplete_file(struct file *f,
|
||||
const struct cred *old_cred = override_creds(mi->mi_owner);
|
||||
int error;
|
||||
|
||||
if (atomic_read(&df->df_data_blocks_written) < df->df_data_block_count)
|
||||
goto out;
|
||||
|
||||
/* Truncate file to remove any preallocated space */
|
||||
bfc = df->df_backing_file_context;
|
||||
if (bfc) {
|
||||
@@ -733,6 +729,7 @@ static long ioctl_fill_blocks(struct file *f, void __user *arg)
|
||||
u8 *data_buf = NULL;
|
||||
ssize_t error = 0;
|
||||
int i = 0;
|
||||
bool complete = false;
|
||||
|
||||
if (!df)
|
||||
return -EBADF;
|
||||
@@ -774,7 +771,7 @@ static long ioctl_fill_blocks(struct file *f, void __user *arg)
|
||||
data_buf);
|
||||
} else {
|
||||
error = incfs_process_new_data_block(df, &fill_block,
|
||||
data_buf);
|
||||
data_buf, &complete);
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
@@ -783,7 +780,8 @@ static long ioctl_fill_blocks(struct file *f, void __user *arg)
|
||||
if (data_buf)
|
||||
free_pages((unsigned long)data_buf, get_order(data_buf_size));
|
||||
|
||||
maybe_delete_incomplete_file(f, df);
|
||||
if (complete)
|
||||
handle_file_completed(f, df);
|
||||
|
||||
/*
|
||||
* Only report the error if no records were processed, otherwise
|
||||
|
||||
Reference in New Issue
Block a user