diff options
author | Ronnie Sahlberg <lsahlber@redhat.com> | 2019-04-25 02:45:29 -0400 |
---|---|---|
committer | Steve French <stfrench@microsoft.com> | 2019-05-08 00:24:55 -0400 |
commit | 2f3ebaba13cebd8badfb9aed31c0cf3cc82eb4f4 (patch) | |
tree | cd2e5193de15784d7e50ce1a42d2a55ae8727f5f | |
parent | d7bef4c4ebe4a2b1788d0214a08d69518e0de3cc (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.c | 1 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 2 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/inode.c | 37 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 77 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 7 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 5 | ||||
-rw-r--r-- | fs/cifs/smbfsctl.h | 2 |
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 | ||
991 | const struct inode_operations cifs_symlink_inode_ops = { | 992 | const 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); | |||
84 | extern int cifs_zap_mapping(struct inode *inode); | 84 | extern int cifs_zap_mapping(struct inode *inode); |
85 | extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int); | 85 | extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int); |
86 | extern int cifs_setattr(struct dentry *, struct iattr *); | 86 | extern int cifs_setattr(struct dentry *, struct iattr *); |
87 | extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, | ||
88 | u64 len); | ||
87 | 89 | ||
88 | extern const struct inode_operations cifs_file_inode_ops; | 90 | extern const struct inode_operations cifs_file_inode_ops; |
89 | extern const struct inode_operations cifs_symlink_inode_ops; | 91 | extern 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 | ||
498 | struct smb_version_values { | 501 | struct 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 | ||
2119 | int 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 | |||
2119 | static int cifs_truncate_page(struct address_space *mapping, loff_t from) | 2156 | static 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 | ||
2889 | static 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 | ||
2890 | static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, | 2963 | static 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 | ||
4059 | struct smb_version_operations smb21_operations = { | 4133 | struct 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 | ||
4158 | struct smb_version_operations smb30_operations = { | 4233 | struct 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 | ||
4266 | struct smb_version_operations smb311_operations = { | 4342 | struct 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 | ||
4375 | struct smb_version_values smb20_values = { | 4452 | struct 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 | ||
871 | struct 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 |