cifs: add fiemap support
Useful for improved copy performance as well as for applications which query allocated ranges of sparse files. Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
d7bef4c4eb
commit
2f3ebaba13
@ -986,6 +986,7 @@ const struct inode_operations cifs_file_inode_ops = {
|
|||||||
.getattr = cifs_getattr,
|
.getattr = cifs_getattr,
|
||||||
.permission = cifs_permission,
|
.permission = cifs_permission,
|
||||||
.listxattr = cifs_listxattr,
|
.listxattr = cifs_listxattr,
|
||||||
|
.fiemap = cifs_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations cifs_symlink_inode_ops = {
|
const struct inode_operations cifs_symlink_inode_ops = {
|
||||||
|
@ -84,6 +84,8 @@ extern int cifs_revalidate_mapping(struct inode *inode);
|
|||||||
extern int cifs_zap_mapping(struct inode *inode);
|
extern int cifs_zap_mapping(struct inode *inode);
|
||||||
extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
|
||||||
extern int cifs_setattr(struct dentry *, struct iattr *);
|
extern int cifs_setattr(struct dentry *, struct iattr *);
|
||||||
|
extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start,
|
||||||
|
u64 len);
|
||||||
|
|
||||||
extern const struct inode_operations cifs_file_inode_ops;
|
extern const struct inode_operations cifs_file_inode_ops;
|
||||||
extern const struct inode_operations cifs_symlink_inode_ops;
|
extern const struct inode_operations cifs_symlink_inode_ops;
|
||||||
|
@ -493,6 +493,9 @@ struct smb_version_operations {
|
|||||||
char *full_path,
|
char *full_path,
|
||||||
umode_t mode,
|
umode_t mode,
|
||||||
dev_t device_number);
|
dev_t device_number);
|
||||||
|
/* version specific fiemap implementation */
|
||||||
|
int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
|
||||||
|
struct fiemap_extent_info *, u64, u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values {
|
struct smb_version_values {
|
||||||
|
@ -2116,6 +2116,43 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
|
||||||
|
u64 len)
|
||||||
|
{
|
||||||
|
struct cifsInodeInfo *cifs_i = CIFS_I(inode);
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->vfs_inode.i_sb);
|
||||||
|
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
struct cifsFileInfo *cfile;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to be sure that all dirty pages are written as they
|
||||||
|
* might fill holes on the server.
|
||||||
|
*/
|
||||||
|
if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
|
||||||
|
inode->i_mapping->nrpages != 0) {
|
||||||
|
rc = filemap_fdatawait(inode->i_mapping);
|
||||||
|
if (rc) {
|
||||||
|
mapping_set_error(inode->i_mapping, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfile = find_readable_file(cifs_i, false);
|
||||||
|
if (cfile == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (server->ops->fiemap) {
|
||||||
|
rc = server->ops->fiemap(tcon, cfile, fei, start, len);
|
||||||
|
cifsFileInfo_put(cfile);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
cifsFileInfo_put(cfile);
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
static int cifs_truncate_page(struct address_space *mapping, loff_t from)
|
static int cifs_truncate_page(struct address_space *mapping, loff_t from)
|
||||||
{
|
{
|
||||||
pgoff_t index = from >> PAGE_SHIFT;
|
pgoff_t index = from >> PAGE_SHIFT;
|
||||||
|
@ -2886,6 +2886,79 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int smb3_fiemap(struct cifs_tcon *tcon,
|
||||||
|
struct cifsFileInfo *cfile,
|
||||||
|
struct fiemap_extent_info *fei, u64 start, u64 len)
|
||||||
|
{
|
||||||
|
unsigned int xid;
|
||||||
|
struct file_allocated_range_buffer in_data, *out_data;
|
||||||
|
u32 out_data_len;
|
||||||
|
int i, num, rc, flags, last_blob;
|
||||||
|
u64 next;
|
||||||
|
|
||||||
|
if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC))
|
||||||
|
return -EBADR;
|
||||||
|
|
||||||
|
xid = get_xid();
|
||||||
|
again:
|
||||||
|
in_data.file_offset = cpu_to_le64(start);
|
||||||
|
in_data.length = cpu_to_le64(len);
|
||||||
|
|
||||||
|
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
|
||||||
|
cfile->fid.volatile_fid,
|
||||||
|
FSCTL_QUERY_ALLOCATED_RANGES, true,
|
||||||
|
(char *)&in_data, sizeof(in_data),
|
||||||
|
1024 * sizeof(struct file_allocated_range_buffer),
|
||||||
|
(char **)&out_data, &out_data_len);
|
||||||
|
if (rc == -E2BIG) {
|
||||||
|
last_blob = 0;
|
||||||
|
rc = 0;
|
||||||
|
} else
|
||||||
|
last_blob = 1;
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (out_data_len % sizeof(struct file_allocated_range_buffer)) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = out_data_len / sizeof(struct file_allocated_range_buffer);
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
flags = 0;
|
||||||
|
if (i == num - 1 && last_blob)
|
||||||
|
flags |= FIEMAP_EXTENT_LAST;
|
||||||
|
|
||||||
|
rc = fiemap_fill_next_extent(fei,
|
||||||
|
le64_to_cpu(out_data[i].file_offset),
|
||||||
|
le64_to_cpu(out_data[i].file_offset),
|
||||||
|
le64_to_cpu(out_data[i].length),
|
||||||
|
flags);
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
if (rc == 1) {
|
||||||
|
rc = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last_blob) {
|
||||||
|
next = le64_to_cpu(out_data[num - 1].file_offset) +
|
||||||
|
le64_to_cpu(out_data[num - 1].length);
|
||||||
|
len = len - (next - start);
|
||||||
|
start = next;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_xid(xid);
|
||||||
|
kfree(out_data);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
|
static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
|
||||||
loff_t off, loff_t len)
|
loff_t off, loff_t len)
|
||||||
@ -4054,6 +4127,7 @@ struct smb_version_operations smb20_operations = {
|
|||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb21_operations = {
|
struct smb_version_operations smb21_operations = {
|
||||||
@ -4153,6 +4227,7 @@ struct smb_version_operations smb21_operations = {
|
|||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb30_operations = {
|
struct smb_version_operations smb30_operations = {
|
||||||
@ -4261,6 +4336,7 @@ struct smb_version_operations smb30_operations = {
|
|||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_operations smb311_operations = {
|
struct smb_version_operations smb311_operations = {
|
||||||
@ -4370,6 +4446,7 @@ struct smb_version_operations smb311_operations = {
|
|||||||
.next_header = smb2_next_header,
|
.next_header = smb2_next_header,
|
||||||
.ioctl_query_info = smb2_ioctl_query_info,
|
.ioctl_query_info = smb2_ioctl_query_info,
|
||||||
.make_node = smb2_make_node,
|
.make_node = smb2_make_node,
|
||||||
|
.fiemap = smb3_fiemap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb20_values = {
|
struct smb_version_values smb20_values = {
|
||||||
|
@ -2622,7 +2622,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||||||
trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
|
trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
|
||||||
ses->Suid, 0, opcode, rc);
|
ses->Suid, 0, opcode, rc);
|
||||||
|
|
||||||
if ((rc != 0) && (rc != -EINVAL)) {
|
if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) {
|
||||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
goto ioctl_exit;
|
goto ioctl_exit;
|
||||||
} else if (rc == -EINVAL) {
|
} else if (rc == -EINVAL) {
|
||||||
@ -2631,6 +2631,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
|
|||||||
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
goto ioctl_exit;
|
goto ioctl_exit;
|
||||||
}
|
}
|
||||||
|
} else if (rc == -E2BIG) {
|
||||||
|
if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) {
|
||||||
|
cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
|
||||||
|
goto ioctl_exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if caller wants to look at return data or just return rc */
|
/* check if caller wants to look at return data or just return rc */
|
||||||
|
@ -868,6 +868,11 @@ struct fsctl_get_integrity_information_rsp {
|
|||||||
__le32 ClusterSizeInBytes;
|
__le32 ClusterSizeInBytes;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
struct file_allocated_range_buffer {
|
||||||
|
__le64 file_offset;
|
||||||
|
__le64 length;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
/* Integrity ChecksumAlgorithm choices for above */
|
/* Integrity ChecksumAlgorithm choices for above */
|
||||||
#define CHECKSUM_TYPE_NONE 0x0000
|
#define CHECKSUM_TYPE_NONE 0x0000
|
||||||
#define CHECKSUM_TYPE_CRC64 0x0002
|
#define CHECKSUM_TYPE_CRC64 0x0002
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
|
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
|
||||||
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
|
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
|
||||||
#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
|
#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
|
||||||
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
|
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF
|
||||||
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
|
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
|
||||||
#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
|
#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
|
||||||
#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
|
#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
|
||||||
|
Loading…
Reference in New Issue
Block a user