mirror of
https://github.com/versity/scoutfs.git
synced 2026-07-20 15:02:21 +00:00
scoutfs: make release block granular
The existing release interface specified byte regions to release but that didn't match what the underlying file data mapping structure is capable of. What happens if you specify a single byte to release? Does it release the whole block? Does it release nothing? Does it return an error? By making the interface match the capability of the operation we make the functioning of the system that much more predictable. Callers are forced to think about implementing their desires in terms of block granular releasing. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
+7
-16
@@ -326,30 +326,19 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
|
||||
struct scoutfs_ioctl_release args;
|
||||
loff_t start;
|
||||
loff_t end_inc;
|
||||
u64 iblock;
|
||||
u64 end_block;
|
||||
u64 len;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&args, (void __user *)arg, sizeof(args)))
|
||||
return -EFAULT;
|
||||
|
||||
trace_printk("offset %llu count %llu vers %llu\n",
|
||||
args.offset, args.count, args.data_version);
|
||||
trace_printk("block %llu count %llu vers %llu\n",
|
||||
args.block, args.count, args.data_version);
|
||||
|
||||
if (args.count == 0)
|
||||
return 0;
|
||||
if ((args.offset + args.count) < args.offset)
|
||||
if ((args.block + args.count) < args.block)
|
||||
return -EINVAL;
|
||||
|
||||
start = round_up(args.offset, SCOUTFS_BLOCK_SIZE);
|
||||
end_inc = round_down(args.offset + args.count, SCOUTFS_BLOCK_SIZE) - 1;
|
||||
if (end_inc < start)
|
||||
return 0;
|
||||
|
||||
iblock = start >> SCOUTFS_BLOCK_SHIFT;
|
||||
end_block = end_inc >> SCOUTFS_BLOCK_SHIFT;
|
||||
len = end_block - iblock + 1;
|
||||
|
||||
ret = mnt_want_write_file(file);
|
||||
if (ret)
|
||||
@@ -375,10 +364,12 @@ static long scoutfs_ioc_release(struct file *file, unsigned long arg)
|
||||
inode_dio_wait(inode);
|
||||
|
||||
/* drop all clean and dirty cached blocks in the range */
|
||||
start = args.block << SCOUTFS_BLOCK_SHIFT;
|
||||
end_inc = ((args.block + args.count) << SCOUTFS_BLOCK_SHIFT) - 1;
|
||||
truncate_inode_pages_range(&inode->i_data, start, end_inc);
|
||||
|
||||
ret = scoutfs_data_truncate_items(sb, scoutfs_ino(inode), iblock, len,
|
||||
true);
|
||||
ret = scoutfs_data_truncate_items(sb, scoutfs_ino(inode), args.block,
|
||||
args.count, true);
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
mnt_drop_write_file(file);
|
||||
|
||||
+23
-1
@@ -124,8 +124,30 @@ struct scoutfs_ioctl_ino_path {
|
||||
|
||||
#define SCOUTFS_IOC_DATA_VERSION _IOW(SCOUTFS_IOCTL_MAGIC, 4, __u64)
|
||||
|
||||
/*
|
||||
* "Release" a contiguous range of logical blocks of file data.
|
||||
* Released blocks are removed from the file system like truncation, but
|
||||
* an offline record is left behind to trigger demand staging if the
|
||||
* file is read.
|
||||
*
|
||||
* The starting block offset and number of blocks to release are in
|
||||
* units 4KB blocks.
|
||||
*
|
||||
* The specified range can extend past i_size and can straddle sparse
|
||||
* regions or blocks that are already offline. The only change it makes
|
||||
* is to free and mark offline any existing blocks that intersect with
|
||||
* the region.
|
||||
*
|
||||
* Returns 0 if the operation succeeds. If an error is returned then
|
||||
* some partial region of the blocks in the region may have been marked
|
||||
* offline.
|
||||
*
|
||||
* If the operation succeeds then inode metadata that reflects file data
|
||||
* contents are not updated. This is intended to be transparent to the
|
||||
* presentation of the data in the file.
|
||||
*/
|
||||
struct scoutfs_ioctl_release {
|
||||
__u64 offset;
|
||||
__u64 block;
|
||||
__u64 count;
|
||||
__u64 data_version;
|
||||
} __packed;
|
||||
|
||||
Reference in New Issue
Block a user