summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRonnie Sahlberg <lsahlber@redhat.com>2019-04-25 02:45:29 -0400
committerSteve French <stfrench@microsoft.com>2019-05-08 00:24:55 -0400
commit2f3ebaba13cebd8badfb9aed31c0cf3cc82eb4f4 (patch)
treecd2e5193de15784d7e50ce1a42d2a55ae8727f5f
parentd7bef4c4ebe4a2b1788d0214a08d69518e0de3cc (diff)
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>
-rw-r--r--fs/cifs/cifsfs.c1
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h3
-rw-r--r--fs/cifs/inode.c37
-rw-r--r--fs/cifs/smb2ops.c77
-rw-r--r--fs/cifs/smb2pdu.c7
-rw-r--r--fs/cifs/smb2pdu.h5
-rw-r--r--fs/cifs/smbfsctl.h2
8 files changed, 132 insertions, 2 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 54c065ada4de..b1a5fcfa3ce1 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -986,6 +986,7 @@ const struct inode_operations cifs_file_inode_ops = {
986 .getattr = cifs_getattr, 986 .getattr = cifs_getattr,
987 .permission = cifs_permission, 987 .permission = cifs_permission,
988 .listxattr = cifs_listxattr, 988 .listxattr = cifs_listxattr,
989 .fiemap = cifs_fiemap,
989}; 990};
990 991
991const struct inode_operations cifs_symlink_inode_ops = { 992const struct inode_operations cifs_symlink_inode_ops = {
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 5c0298b9998f..c47d93d74d75 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -84,6 +84,8 @@ extern int cifs_revalidate_mapping(struct inode *inode);
84extern int cifs_zap_mapping(struct inode *inode); 84extern int cifs_zap_mapping(struct inode *inode);
85extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int); 85extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int);
86extern int cifs_setattr(struct dentry *, struct iattr *); 86extern int cifs_setattr(struct dentry *, struct iattr *);
87extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start,
88 u64 len);
87 89
88extern const struct inode_operations cifs_file_inode_ops; 90extern const struct inode_operations cifs_file_inode_ops;
89extern const struct inode_operations cifs_symlink_inode_ops; 91extern const struct inode_operations cifs_symlink_inode_ops;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 0dc55f4e6929..5ffe0e538cec 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -493,6 +493,9 @@ struct smb_version_operations {
493 char *full_path, 493 char *full_path,
494 umode_t mode, 494 umode_t mode,
495 dev_t device_number); 495 dev_t device_number);
496 /* version specific fiemap implementation */
497 int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *,
498 struct fiemap_extent_info *, u64, u64);
496}; 499};
497 500
498struct smb_version_values { 501struct smb_version_values {
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 538fd7d807e4..d7cc62252634 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -2116,6 +2116,43 @@ int cifs_getattr(const struct path *path, struct kstat *stat,
2116 return rc; 2116 return rc;
2117} 2117}
2118 2118
2119int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
2120 u64 len)
2121{
2122 struct cifsInodeInfo *cifs_i = CIFS_I(inode);
2123 struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->vfs_inode.i_sb);
2124 struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
2125 struct TCP_Server_Info *server = tcon->ses->server;
2126 struct cifsFileInfo *cfile;
2127 int rc;
2128
2129 /*
2130 * We need to be sure that all dirty pages are written as they
2131 * might fill holes on the server.
2132 */
2133 if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
2134 inode->i_mapping->nrpages != 0) {
2135 rc = filemap_fdatawait(inode->i_mapping);
2136 if (rc) {
2137 mapping_set_error(inode->i_mapping, rc);
2138 return rc;
2139 }
2140 }
2141
2142 cfile = find_readable_file(cifs_i, false);
2143 if (cfile == NULL)
2144 return -EINVAL;
2145
2146 if (server->ops->fiemap) {
2147 rc = server->ops->fiemap(tcon, cfile, fei, start, len);
2148 cifsFileInfo_put(cfile);
2149 return rc;
2150 }
2151
2152 cifsFileInfo_put(cfile);
2153 return -ENOTSUPP;
2154}
2155
2119static int cifs_truncate_page(struct address_space *mapping, loff_t from) 2156static int cifs_truncate_page(struct address_space *mapping, loff_t from)
2120{ 2157{
2121 pgoff_t index = from >> PAGE_SHIFT; 2158 pgoff_t index = from >> PAGE_SHIFT;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 4002e1433ccb..78bca7d46eac 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2886,6 +2886,79 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
2886 return rc; 2886 return rc;
2887} 2887}
2888 2888
2889static int smb3_fiemap(struct cifs_tcon *tcon,
2890 struct cifsFileInfo *cfile,
2891 struct fiemap_extent_info *fei, u64 start, u64 len)
2892{
2893 unsigned int xid;
2894 struct file_allocated_range_buffer in_data, *out_data;
2895 u32 out_data_len;
2896 int i, num, rc, flags, last_blob;
2897 u64 next;
2898
2899 if (fiemap_check_flags(fei, FIEMAP_FLAG_SYNC))
2900 return -EBADR;
2901
2902 xid = get_xid();
2903 again:
2904 in_data.file_offset = cpu_to_le64(start);
2905 in_data.length = cpu_to_le64(len);
2906
2907 rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
2908 cfile->fid.volatile_fid,
2909 FSCTL_QUERY_ALLOCATED_RANGES, true,
2910 (char *)&in_data, sizeof(in_data),
2911 1024 * sizeof(struct file_allocated_range_buffer),
2912 (char **)&out_data, &out_data_len);
2913 if (rc == -E2BIG) {
2914 last_blob = 0;
2915 rc = 0;
2916 } else
2917 last_blob = 1;
2918 if (rc)
2919 goto out;
2920
2921 if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
2922 rc = -EINVAL;
2923 goto out;
2924 }
2925 if (out_data_len % sizeof(struct file_allocated_range_buffer)) {
2926 rc = -EINVAL;
2927 goto out;
2928 }
2929
2930 num = out_data_len / sizeof(struct file_allocated_range_buffer);
2931 for (i = 0; i < num; i++) {
2932 flags = 0;
2933 if (i == num - 1 && last_blob)
2934 flags |= FIEMAP_EXTENT_LAST;
2935
2936 rc = fiemap_fill_next_extent(fei,
2937 le64_to_cpu(out_data[i].file_offset),
2938 le64_to_cpu(out_data[i].file_offset),
2939 le64_to_cpu(out_data[i].length),
2940 flags);
2941 if (rc < 0)
2942 goto out;
2943 if (rc == 1) {
2944 rc = 0;
2945 goto out;
2946 }
2947 }
2948
2949 if (!last_blob) {
2950 next = le64_to_cpu(out_data[num - 1].file_offset) +
2951 le64_to_cpu(out_data[num - 1].length);
2952 len = len - (next - start);
2953 start = next;
2954 goto again;
2955 }
2956
2957 out:
2958 free_xid(xid);
2959 kfree(out_data);
2960 return rc;
2961}
2889 2962
2890static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, 2963static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
2891 loff_t off, loff_t len) 2964 loff_t off, loff_t len)
@@ -4054,6 +4127,7 @@ struct smb_version_operations smb20_operations = {
4054 .next_header = smb2_next_header, 4127 .next_header = smb2_next_header,
4055 .ioctl_query_info = smb2_ioctl_query_info, 4128 .ioctl_query_info = smb2_ioctl_query_info,
4056 .make_node = smb2_make_node, 4129 .make_node = smb2_make_node,
4130 .fiemap = smb3_fiemap,
4057}; 4131};
4058 4132
4059struct smb_version_operations smb21_operations = { 4133struct smb_version_operations smb21_operations = {
@@ -4153,6 +4227,7 @@ struct smb_version_operations smb21_operations = {
4153 .next_header = smb2_next_header, 4227 .next_header = smb2_next_header,
4154 .ioctl_query_info = smb2_ioctl_query_info, 4228 .ioctl_query_info = smb2_ioctl_query_info,
4155 .make_node = smb2_make_node, 4229 .make_node = smb2_make_node,
4230 .fiemap = smb3_fiemap,
4156}; 4231};
4157 4232
4158struct smb_version_operations smb30_operations = { 4233struct smb_version_operations smb30_operations = {
@@ -4261,6 +4336,7 @@ struct smb_version_operations smb30_operations = {
4261 .next_header = smb2_next_header, 4336 .next_header = smb2_next_header,
4262 .ioctl_query_info = smb2_ioctl_query_info, 4337 .ioctl_query_info = smb2_ioctl_query_info,
4263 .make_node = smb2_make_node, 4338 .make_node = smb2_make_node,
4339 .fiemap = smb3_fiemap,
4264}; 4340};
4265 4341
4266struct smb_version_operations smb311_operations = { 4342struct smb_version_operations smb311_operations = {
@@ -4370,6 +4446,7 @@ struct smb_version_operations smb311_operations = {
4370 .next_header = smb2_next_header, 4446 .next_header = smb2_next_header,
4371 .ioctl_query_info = smb2_ioctl_query_info, 4447 .ioctl_query_info = smb2_ioctl_query_info,
4372 .make_node = smb2_make_node, 4448 .make_node = smb2_make_node,
4449 .fiemap = smb3_fiemap,
4373}; 4450};
4374 4451
4375struct smb_version_values smb20_values = { 4452struct smb_version_values smb20_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index a932dccafc5b..634800c0bc06 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2622,7 +2622,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
2622 trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid, 2622 trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
2623 ses->Suid, 0, opcode, rc); 2623 ses->Suid, 0, opcode, rc);
2624 2624
2625 if ((rc != 0) && (rc != -EINVAL)) { 2625 if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) {
2626 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 2626 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
2627 goto ioctl_exit; 2627 goto ioctl_exit;
2628 } else if (rc == -EINVAL) { 2628 } else if (rc == -EINVAL) {
@@ -2631,6 +2631,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
2631 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); 2631 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
2632 goto ioctl_exit; 2632 goto ioctl_exit;
2633 } 2633 }
2634 } else if (rc == -E2BIG) {
2635 if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) {
2636 cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
2637 goto ioctl_exit;
2638 }
2634 } 2639 }
2635 2640
2636 /* check if caller wants to look at return data or just return rc */ 2641 /* check if caller wants to look at return data or just return rc */
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 58158e91b9d1..82686e9d9e05 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -868,6 +868,11 @@ struct fsctl_get_integrity_information_rsp {
868 __le32 ClusterSizeInBytes; 868 __le32 ClusterSizeInBytes;
869} __packed; 869} __packed;
870 870
871struct file_allocated_range_buffer {
872 __le64 file_offset;
873 __le64 length;
874} __packed;
875
871/* Integrity ChecksumAlgorithm choices for above */ 876/* Integrity ChecksumAlgorithm choices for above */
872#define CHECKSUM_TYPE_NONE 0x0000 877#define CHECKSUM_TYPE_NONE 0x0000
873#define CHECKSUM_TYPE_CRC64 0x0002 878#define CHECKSUM_TYPE_CRC64 0x0002
diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h
index 9b3459b9a5ce..08628e6a42ac 100644
--- a/fs/cifs/smbfsctl.h
+++ b/fs/cifs/smbfsctl.h
@@ -103,7 +103,7 @@
103#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */ 103#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
104#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */ 104#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
105#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C 105#define FSCTL_GET_INTEGRITY_INFORMATION 0x0009027C
106#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */ 106#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF
107#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */ 107#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
108#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */ 108#define FSCTL_FILE_LEVEL_TRIM 0x00098208 /* BB add struct */
109#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344 109#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344