Commit | Line | Data |
---|---|---|
929be906 | 1 | // SPDX-License-Identifier: LGPL-2.1 |
488f1d2d | 2 | /* |
099dd788 | 3 | * CIFS filesystem cache interface |
488f1d2d SJ |
4 | * |
5 | * Copyright (c) 2010 Novell, Inc. | |
b81209de | 6 | * Author(s): Suresh Jayaraman <sjayaraman@suse.de> |
488f1d2d | 7 | * |
488f1d2d SJ |
8 | */ |
9 | #include "fscache.h" | |
10 | #include "cifsglob.h" | |
11 | #include "cifs_debug.h" | |
9451a9a5 | 12 | #include "cifs_fs_sb.h" |
e73a42e0 | 13 | #include "cifsproto.h" |
488f1d2d | 14 | |
8876a372 DH |
15 | /* |
16 | * Key for fscache inode. [!] Contents must match comparisons in cifs_find_inode(). | |
17 | */ | |
18 | struct cifs_fscache_inode_key { | |
19 | ||
20 | __le64 uniqueid; /* server inode number */ | |
21 | __le64 createtime; /* creation time on server */ | |
22 | u8 type; /* S_IFMT file type */ | |
23 | } __packed; | |
24 | ||
70431bfd DH |
25 | static void cifs_fscache_fill_volume_coherency( |
26 | struct cifs_tcon *tcon, | |
27 | struct cifs_fscache_volume_coherency_data *cd) | |
488f1d2d | 28 | { |
70431bfd DH |
29 | memset(cd, 0, sizeof(*cd)); |
30 | cd->resource_id = cpu_to_le64(tcon->resource_id); | |
31 | cd->vol_create_time = tcon->vol_create_time; | |
32 | cd->vol_serial_number = cpu_to_le32(tcon->vol_serial_number); | |
488f1d2d SJ |
33 | } |
34 | ||
70431bfd | 35 | int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) |
d03382ce | 36 | { |
70431bfd | 37 | struct cifs_fscache_volume_coherency_data cd; |
d03382ce | 38 | struct TCP_Server_Info *server = tcon->ses->server; |
70431bfd DH |
39 | struct fscache_volume *vcookie; |
40 | const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; | |
41 | size_t slen, i; | |
402cb8dd | 42 | char *sharename; |
70431bfd DH |
43 | char *key; |
44 | int ret = -ENOMEM; | |
402cb8dd | 45 | |
dad80c6b DH |
46 | if (tcon->fscache_acquired) |
47 | return 0; | |
48 | ||
49 | mutex_lock(&tcon->fscache_lock); | |
50 | if (tcon->fscache_acquired) { | |
51 | mutex_unlock(&tcon->fscache_lock); | |
52 | return 0; | |
53 | } | |
54 | tcon->fscache_acquired = true; | |
55 | ||
70431bfd DH |
56 | tcon->fscache = NULL; |
57 | switch (sa->sa_family) { | |
58 | case AF_INET: | |
59 | case AF_INET6: | |
60 | break; | |
61 | default: | |
dad80c6b | 62 | mutex_unlock(&tcon->fscache_lock); |
70431bfd DH |
63 | cifs_dbg(VFS, "Unknown network family '%d'\n", sa->sa_family); |
64 | return -EINVAL; | |
65 | } | |
66 | ||
67 | memset(&key, 0, sizeof(key)); | |
02102744 | 68 | |
68e14569 | 69 | sharename = extract_sharename(tcon->tree_name); |
402cb8dd | 70 | if (IS_ERR(sharename)) { |
dad80c6b | 71 | mutex_unlock(&tcon->fscache_lock); |
402cb8dd | 72 | cifs_dbg(FYI, "%s: couldn't extract sharename\n", __func__); |
efc0b0bc | 73 | return PTR_ERR(sharename); |
402cb8dd | 74 | } |
d03382ce | 75 | |
70431bfd DH |
76 | slen = strlen(sharename); |
77 | for (i = 0; i < slen; i++) | |
78 | if (sharename[i] == '/') | |
79 | sharename[i] = ';'; | |
80 | ||
81 | key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); | |
82 | if (!key) | |
83 | goto out; | |
84 | ||
85 | cifs_fscache_fill_volume_coherency(tcon, &cd); | |
86 | vcookie = fscache_acquire_volume(key, | |
87 | NULL, /* preferred_cache */ | |
88 | &cd, sizeof(cd)); | |
89 | cifs_dbg(FYI, "%s: (%s/0x%p)\n", __func__, key, vcookie); | |
90 | if (IS_ERR(vcookie)) { | |
91 | if (vcookie != ERR_PTR(-EBUSY)) { | |
92 | ret = PTR_ERR(vcookie); | |
93 | goto out_2; | |
94 | } | |
95 | pr_err("Cache volume key already in use (%s)\n", key); | |
96 | vcookie = NULL; | |
afc23feb DH |
97 | trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, |
98 | netfs_trace_tcon_ref_see_fscache_collision); | |
99 | } else { | |
100 | trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, | |
101 | netfs_trace_tcon_ref_see_fscache_okay); | |
70431bfd | 102 | } |
58659854 | 103 | |
70431bfd DH |
104 | tcon->fscache = vcookie; |
105 | ret = 0; | |
106 | out_2: | |
107 | kfree(key); | |
108 | out: | |
402cb8dd | 109 | kfree(sharename); |
dad80c6b | 110 | mutex_unlock(&tcon->fscache_lock); |
70431bfd | 111 | return ret; |
d03382ce SJ |
112 | } |
113 | ||
96daf2b0 | 114 | void cifs_fscache_release_super_cookie(struct cifs_tcon *tcon) |
d03382ce | 115 | { |
70431bfd | 116 | struct cifs_fscache_volume_coherency_data cd; |
58659854 | 117 | |
f96637be | 118 | cifs_dbg(FYI, "%s: (0x%p)\n", __func__, tcon->fscache); |
402cb8dd | 119 | |
70431bfd DH |
120 | cifs_fscache_fill_volume_coherency(tcon, &cd); |
121 | fscache_relinquish_volume(tcon->fscache, &cd, false); | |
122 | tcon->fscache = NULL; | |
afc23feb DH |
123 | trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, |
124 | netfs_trace_tcon_ref_see_fscache_relinq); | |
402cb8dd DH |
125 | } |
126 | ||
70431bfd | 127 | void cifs_fscache_get_inode_cookie(struct inode *inode) |
9451a9a5 | 128 | { |
70431bfd | 129 | struct cifs_fscache_inode_coherency_data cd; |
8876a372 | 130 | struct cifs_fscache_inode_key key; |
9451a9a5 SJ |
131 | struct cifsInodeInfo *cifsi = CIFS_I(inode); |
132 | struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); | |
96daf2b0 | 133 | struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); |
9451a9a5 | 134 | |
8876a372 DH |
135 | key.uniqueid = cpu_to_le64(cifsi->uniqueid); |
136 | key.createtime = cpu_to_le64(cifsi->createtime); | |
137 | key.type = (inode->i_mode & S_IFMT) >> 12; | |
874c8ca1 | 138 | cifs_fscache_fill_coherency(&cifsi->netfs.inode, &cd); |
402cb8dd | 139 | |
874c8ca1 | 140 | cifsi->netfs.cache = |
70431bfd | 141 | fscache_acquire_cookie(tcon->fscache, 0, |
8876a372 | 142 | &key, sizeof(key), |
70431bfd | 143 | &cd, sizeof(cd), |
874c8ca1 | 144 | i_size_read(&cifsi->netfs.inode)); |
b4fa966f DH |
145 | if (cifsi->netfs.cache) |
146 | mapping_set_release_always(inode->i_mapping); | |
9451a9a5 SJ |
147 | } |
148 | ||
70431bfd | 149 | void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update) |
9451a9a5 | 150 | { |
70431bfd DH |
151 | if (update) { |
152 | struct cifs_fscache_inode_coherency_data cd; | |
153 | loff_t i_size = i_size_read(inode); | |
402cb8dd | 154 | |
70431bfd DH |
155 | cifs_fscache_fill_coherency(inode, &cd); |
156 | fscache_unuse_cookie(cifs_inode_cookie(inode), &cd, &i_size); | |
157 | } else { | |
158 | fscache_unuse_cookie(cifs_inode_cookie(inode), NULL, NULL); | |
9451a9a5 SJ |
159 | } |
160 | } | |
161 | ||
70431bfd | 162 | void cifs_fscache_release_inode_cookie(struct inode *inode) |
9451a9a5 SJ |
163 | { |
164 | struct cifsInodeInfo *cifsi = CIFS_I(inode); | |
bc899ee1 | 165 | struct fscache_cookie *cookie = cifs_inode_cookie(inode); |
9451a9a5 | 166 | |
bc899ee1 DH |
167 | if (cookie) { |
168 | cifs_dbg(FYI, "%s: (0x%p)\n", __func__, cookie); | |
169 | fscache_relinquish_cookie(cookie, false); | |
874c8ca1 | 170 | cifsi->netfs.cache = NULL; |
9451a9a5 SJ |
171 | } |
172 | } | |
85f2d6b4 | 173 | |
56698236 | 174 | /* |
0174ee99 | 175 | * Fallback page reading interface. |
56698236 | 176 | */ |
0174ee99 | 177 | static int fscache_fallback_read_page(struct inode *inode, struct page *page) |
56698236 | 178 | { |
0174ee99 DH |
179 | struct netfs_cache_resources cres; |
180 | struct fscache_cookie *cookie = cifs_inode_cookie(inode); | |
181 | struct iov_iter iter; | |
220ae4a5 | 182 | struct bio_vec bvec; |
0174ee99 DH |
183 | int ret; |
184 | ||
185 | memset(&cres, 0, sizeof(cres)); | |
220ae4a5 CH |
186 | bvec_set_page(&bvec, page, PAGE_SIZE, 0); |
187 | iov_iter_bvec(&iter, ITER_DEST, &bvec, 1, PAGE_SIZE); | |
0174ee99 DH |
188 | |
189 | ret = fscache_begin_read_operation(&cres, cookie); | |
190 | if (ret < 0) | |
191 | return ret; | |
192 | ||
193 | ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, | |
194 | NULL, NULL); | |
195 | fscache_end_operation(&cres); | |
196 | return ret; | |
56698236 SJ |
197 | } |
198 | ||
199 | /* | |
0174ee99 | 200 | * Fallback page writing interface. |
56698236 | 201 | */ |
d08089f6 DH |
202 | static int fscache_fallback_write_pages(struct inode *inode, loff_t start, size_t len, |
203 | bool no_space_allocated_yet) | |
56698236 | 204 | { |
0174ee99 DH |
205 | struct netfs_cache_resources cres; |
206 | struct fscache_cookie *cookie = cifs_inode_cookie(inode); | |
207 | struct iov_iter iter; | |
0174ee99 DH |
208 | int ret; |
209 | ||
210 | memset(&cres, 0, sizeof(cres)); | |
d08089f6 | 211 | iov_iter_xarray(&iter, ITER_SOURCE, &inode->i_mapping->i_pages, start, len); |
0174ee99 DH |
212 | |
213 | ret = fscache_begin_write_operation(&cres, cookie); | |
214 | if (ret < 0) | |
215 | return ret; | |
216 | ||
e0ace6ca | 217 | ret = cres.ops->prepare_write(&cres, &start, &len, len, i_size_read(inode), |
0174ee99 DH |
218 | no_space_allocated_yet); |
219 | if (ret == 0) | |
d08089f6 | 220 | ret = fscache_write(&cres, start, &iter, NULL, NULL); |
0174ee99 DH |
221 | fscache_end_operation(&cres); |
222 | return ret; | |
56698236 SJ |
223 | } |
224 | ||
0174ee99 DH |
225 | /* |
226 | * Retrieve a page from FS-Cache | |
227 | */ | |
228 | int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) | |
9dc06558 | 229 | { |
0174ee99 | 230 | int ret; |
9dc06558 | 231 | |
0174ee99 DH |
232 | cifs_dbg(FYI, "%s: (fsc:%p, p:%p, i:0x%p\n", |
233 | __func__, cifs_inode_cookie(inode), page, inode); | |
18d04062 | 234 | |
0174ee99 DH |
235 | ret = fscache_fallback_read_page(inode, page); |
236 | if (ret < 0) | |
237 | return ret; | |
238 | ||
239 | /* Read completed synchronously */ | |
240 | SetPageUptodate(page); | |
241 | return 0; | |
242 | } | |
243 | ||
d08089f6 | 244 | void __cifs_readahead_to_fscache(struct inode *inode, loff_t pos, size_t len) |
0174ee99 | 245 | { |
d08089f6 DH |
246 | cifs_dbg(FYI, "%s: (fsc: %p, p: %llx, l: %zx, i: %p)\n", |
247 | __func__, cifs_inode_cookie(inode), pos, len, inode); | |
0174ee99 | 248 | |
d08089f6 | 249 | fscache_fallback_write_pages(inode, pos, len, true); |
0174ee99 DH |
250 | } |
251 | ||
252 | /* | |
253 | * Query the cache occupancy. | |
254 | */ | |
255 | int __cifs_fscache_query_occupancy(struct inode *inode, | |
256 | pgoff_t first, unsigned int nr_pages, | |
257 | pgoff_t *_data_first, | |
258 | unsigned int *_data_nr_pages) | |
259 | { | |
260 | struct netfs_cache_resources cres; | |
261 | struct fscache_cookie *cookie = cifs_inode_cookie(inode); | |
262 | loff_t start, data_start; | |
263 | size_t len, data_len; | |
264 | int ret; | |
18d04062 | 265 | |
0174ee99 DH |
266 | ret = fscache_begin_read_operation(&cres, cookie); |
267 | if (ret < 0) | |
268 | return ret; | |
269 | ||
270 | start = first * PAGE_SIZE; | |
271 | len = nr_pages * PAGE_SIZE; | |
272 | ret = cres.ops->query_occupancy(&cres, start, len, PAGE_SIZE, | |
273 | &data_start, &data_len); | |
274 | if (ret == 0) { | |
275 | *_data_first = data_start / PAGE_SIZE; | |
276 | *_data_nr_pages = len / PAGE_SIZE; | |
277 | } | |
278 | ||
279 | fscache_end_operation(&cres); | |
280 | return ret; | |
18d04062 | 281 | } |