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:
Namjae Jeon 2026-07-12 00:11:39 +09:00
parent 8b57448a14
commit a72692c5bc
4 changed files with 125 additions and 1 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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,