mirror of
https://github.com/torvalds/linux.git
synced 2026-10-08 03:26:02 +02:00
ksmbd: support file level trim
Advertise trim support through FS_SECTOR_SIZE_INFORMATION and handle FSCTL_FILE_LEVEL_TRIM requests. Process each trim range by punching a hole while keeping the file size unchanged, and report the number of ranges completed in the ioctl response. The trim operation uses the same byte-range lock handling as zero data for the affected part of the file. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
8b57448a14
commit
a72692c5bc
|
|
@ -202,6 +202,21 @@ struct file_zero_data_information {
|
|||
__le64 BeyondFinalZero;
|
||||
} __packed;
|
||||
|
||||
struct file_level_trim_range {
|
||||
__le64 Offset;
|
||||
__le64 Length;
|
||||
} __packed;
|
||||
|
||||
struct file_level_trim {
|
||||
__le32 Key;
|
||||
__le32 NumRanges;
|
||||
struct file_level_trim_range Ranges[];
|
||||
} __packed;
|
||||
|
||||
struct file_level_trim_output {
|
||||
__le32 NumRangesProcessed;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* This level 18, although with struct with same name is different from cifs
|
||||
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
|
||||
|
|
|
|||
|
|
@ -6634,7 +6634,8 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
|
|||
info->FSEffPhysicalBytesPerSectorForAtomicity =
|
||||
cpu_to_le32(sector_size);
|
||||
info->Flags = cpu_to_le32(SSINFO_FLAGS_ALIGNED_DEVICE |
|
||||
SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE);
|
||||
SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE |
|
||||
SSINFO_FLAGS_TRIM_ENABLED);
|
||||
info->ByteOffsetForSectorAlignment = 0;
|
||||
info->ByteOffsetForPartitionAlignment = 0;
|
||||
rsp->OutputBufferLength = cpu_to_le32(28);
|
||||
|
|
@ -9791,6 +9792,85 @@ int smb2_ioctl(struct ksmbd_work *work)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FSCTL_FILE_LEVEL_TRIM:
|
||||
{
|
||||
struct file_level_trim *trim_req;
|
||||
struct file_level_trim_output *trim_rsp;
|
||||
struct ksmbd_file *fp;
|
||||
u32 i, num_ranges;
|
||||
|
||||
if (!test_tree_conn_flag(work->tcon, KSMBD_TREE_CONN_FLAG_WRITABLE)) {
|
||||
ksmbd_debug(SMB,
|
||||
"User does not have write permission\n");
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (in_buf_len < offsetof(struct file_level_trim, Ranges)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (out_buf_len < sizeof(struct file_level_trim_output)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
trim_req = (struct file_level_trim *)buffer;
|
||||
num_ranges = le32_to_cpu(trim_req->NumRanges);
|
||||
if (num_ranges >
|
||||
(in_buf_len - offsetof(struct file_level_trim, Ranges)) /
|
||||
sizeof(struct file_level_trim_range)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fp = ksmbd_lookup_fd_fast(work, id);
|
||||
if (!fp) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
|
||||
ksmbd_fd_put(work, fp);
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
trim_rsp = (struct file_level_trim_output *)&rsp->Buffer[0];
|
||||
trim_rsp->NumRangesProcessed = 0;
|
||||
for (i = 0; i < num_ranges; i++) {
|
||||
loff_t off = le64_to_cpu(trim_req->Ranges[i].Offset);
|
||||
loff_t len = le64_to_cpu(trim_req->Ranges[i].Length);
|
||||
|
||||
if (off < 0 || len < 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
trim_rsp->NumRangesProcessed =
|
||||
cpu_to_le32(i + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ksmbd_vfs_trim_data(work, fp, off, len);
|
||||
if (ret)
|
||||
break;
|
||||
trim_rsp->NumRangesProcessed = cpu_to_le32(i + 1);
|
||||
}
|
||||
ksmbd_fd_put(work, fp);
|
||||
if (ret == -EAGAIN) {
|
||||
rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT;
|
||||
ret = 0;
|
||||
goto out;
|
||||
} else if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
nbytes = sizeof(struct file_level_trim_output);
|
||||
break;
|
||||
}
|
||||
case FSCTL_QUERY_ALLOCATED_RANGES:
|
||||
if (in_buf_len < sizeof(struct file_allocated_range_buffer)) {
|
||||
ret = -EINVAL;
|
||||
|
|
|
|||
|
|
@ -953,6 +953,33 @@ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
|||
return err;
|
||||
}
|
||||
|
||||
int ksmbd_vfs_trim_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
loff_t off, loff_t len)
|
||||
{
|
||||
const struct cred *saved_cred;
|
||||
int err;
|
||||
|
||||
smb_break_all_levII_oplock(work, fp, 1);
|
||||
if (!work->tcon->posix_extensions) {
|
||||
loff_t size = i_size_read(file_inode(fp->filp));
|
||||
|
||||
if (off < size) {
|
||||
err = check_lock_range(fp->filp, off,
|
||||
min(off + len, size) - 1,
|
||||
WRITE);
|
||||
if (err)
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
saved_cred = override_creds(fp->filp->f_cred);
|
||||
err = vfs_fallocate(fp->filp,
|
||||
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
|
||||
off, len);
|
||||
revert_creds(saved_cred);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
|
||||
struct file_allocated_range_buffer *ranges,
|
||||
unsigned int in_count, unsigned int *out_count)
|
||||
|
|
|
|||
|
|
@ -133,6 +133,8 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
|
|||
void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
|
||||
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
loff_t off, loff_t len);
|
||||
int ksmbd_vfs_trim_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
loff_t off, loff_t len);
|
||||
struct file_allocated_range_buffer;
|
||||
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
|
||||
struct file_allocated_range_buffer *ranges,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user