]>
Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
14727281 DH |
2 | /* NFS filesystem cache interface |
3 | * | |
4 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells ([email protected]) | |
14727281 DH |
6 | */ |
7 | ||
8 | #include <linux/init.h> | |
9 | #include <linux/kernel.h> | |
10 | #include <linux/sched.h> | |
11 | #include <linux/mm.h> | |
12 | #include <linux/nfs_fs.h> | |
13 | #include <linux/nfs_fs_sb.h> | |
14 | #include <linux/in6.h> | |
15 | #include <linux/seq_file.h> | |
5a0e3ad6 | 16 | #include <linux/slab.h> |
402cb8dd | 17 | #include <linux/iversion.h> |
14727281 DH |
18 | |
19 | #include "internal.h" | |
545db45f | 20 | #include "iostat.h" |
14727281 | 21 | #include "fscache.h" |
e3f0a7fe | 22 | #include "nfstrace.h" |
14727281 | 23 | |
a6b5a28e | 24 | #define NFS_MAX_KEY_LEN 1000 |
08734048 | 25 | |
a6b5a28e DW |
26 | static bool nfs_append_int(char *key, int *_len, unsigned long long x) |
27 | { | |
28 | if (*_len > NFS_MAX_KEY_LEN) | |
29 | return false; | |
30 | if (x == 0) | |
31 | key[(*_len)++] = ','; | |
32 | else | |
33 | *_len += sprintf(key + *_len, ",%llx", x); | |
34 | return true; | |
35 | } | |
402cb8dd | 36 | |
14727281 DH |
37 | /* |
38 | * Get the per-client index cookie for an NFS client if the appropriate mount | |
39 | * flag was set | |
40 | * - We always try and get an index cookie for the client, but get filehandle | |
41 | * cookies on a per-superblock basis, depending on the mount flags | |
42 | */ | |
a6b5a28e DW |
43 | static bool nfs_fscache_get_client_key(struct nfs_client *clp, |
44 | char *key, int *_len) | |
14727281 | 45 | { |
402cb8dd DH |
46 | const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &clp->cl_addr; |
47 | const struct sockaddr_in *sin = (struct sockaddr_in *) &clp->cl_addr; | |
402cb8dd | 48 | |
a6b5a28e DW |
49 | *_len += snprintf(key + *_len, NFS_MAX_KEY_LEN - *_len, |
50 | ",%u.%u,%x", | |
51 | clp->rpc_ops->version, | |
52 | clp->cl_minorversion, | |
53 | clp->cl_addr.ss_family); | |
402cb8dd DH |
54 | |
55 | switch (clp->cl_addr.ss_family) { | |
56 | case AF_INET: | |
a6b5a28e DW |
57 | if (!nfs_append_int(key, _len, sin->sin_port) || |
58 | !nfs_append_int(key, _len, sin->sin_addr.s_addr)) | |
59 | return false; | |
60 | return true; | |
402cb8dd DH |
61 | |
62 | case AF_INET6: | |
a6b5a28e DW |
63 | if (!nfs_append_int(key, _len, sin6->sin6_port) || |
64 | !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[0]) || | |
65 | !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[1]) || | |
66 | !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[2]) || | |
67 | !nfs_append_int(key, _len, sin6->sin6_addr.s6_addr32[3])) | |
68 | return false; | |
69 | return true; | |
402cb8dd DH |
70 | |
71 | default: | |
72 | printk(KERN_WARNING "NFS: Unknown network family '%d'\n", | |
73 | clp->cl_addr.ss_family); | |
a6b5a28e | 74 | return false; |
402cb8dd | 75 | } |
14727281 | 76 | } |
08734048 DH |
77 | |
78 | /* | |
a6b5a28e | 79 | * Get the cache cookie for an NFS superblock. |
2df54806 DH |
80 | * |
81 | * The default uniquifier is just an empty string, but it may be overridden | |
82 | * either by the 'fsc=xxx' option to mount, or by inheriting it from the parent | |
83 | * superblock across an automount point of some nature. | |
08734048 | 84 | */ |
a6b5a28e | 85 | int nfs_fscache_get_super_cookie(struct super_block *sb, const char *uniq, int ulen) |
08734048 | 86 | { |
a6b5a28e | 87 | struct fscache_volume *vcookie; |
08734048 | 88 | struct nfs_server *nfss = NFS_SB(sb); |
a6b5a28e DW |
89 | unsigned int len = 3; |
90 | char *key; | |
2df54806 | 91 | |
a6b5a28e DW |
92 | if (uniq) { |
93 | nfss->fscache_uniq = kmemdup_nul(uniq, ulen, GFP_KERNEL); | |
94 | if (!nfss->fscache_uniq) | |
95 | return -ENOMEM; | |
2df54806 DH |
96 | } |
97 | ||
a6b5a28e | 98 | key = kmalloc(NFS_MAX_KEY_LEN + 24, GFP_KERNEL); |
08734048 | 99 | if (!key) |
a6b5a28e DW |
100 | return -ENOMEM; |
101 | ||
102 | memcpy(key, "nfs", 3); | |
103 | if (!nfs_fscache_get_client_key(nfss->nfs_client, key, &len) || | |
104 | !nfs_append_int(key, &len, nfss->fsid.major) || | |
105 | !nfs_append_int(key, &len, nfss->fsid.minor) || | |
106 | !nfs_append_int(key, &len, sb->s_flags & NFS_SB_MASK) || | |
107 | !nfs_append_int(key, &len, nfss->flags) || | |
108 | !nfs_append_int(key, &len, nfss->rsize) || | |
109 | !nfs_append_int(key, &len, nfss->wsize) || | |
110 | !nfs_append_int(key, &len, nfss->acregmin) || | |
111 | !nfs_append_int(key, &len, nfss->acregmax) || | |
112 | !nfs_append_int(key, &len, nfss->acdirmin) || | |
113 | !nfs_append_int(key, &len, nfss->acdirmax) || | |
114 | !nfs_append_int(key, &len, nfss->client->cl_auth->au_flavor)) | |
115 | goto out; | |
116 | ||
117 | if (ulen > 0) { | |
118 | if (ulen > NFS_MAX_KEY_LEN - len) | |
119 | goto out; | |
120 | key[len++] = ','; | |
121 | memcpy(key + len, uniq, ulen); | |
122 | len += ulen; | |
08734048 | 123 | } |
a6b5a28e | 124 | key[len] = 0; |
08734048 DH |
125 | |
126 | /* create a cache index for looking up filehandles */ | |
a6b5a28e DW |
127 | vcookie = fscache_acquire_volume(key, |
128 | NULL, /* preferred_cache */ | |
129 | NULL, 0 /* coherency_data */); | |
a6b5a28e DW |
130 | if (IS_ERR(vcookie)) { |
131 | if (vcookie != ERR_PTR(-EBUSY)) { | |
132 | kfree(key); | |
133 | return PTR_ERR(vcookie); | |
134 | } | |
135 | pr_err("NFS: Cache volume key already in use (%s)\n", key); | |
136 | vcookie = NULL; | |
137 | } | |
138 | nfss->fscache = vcookie; | |
08734048 | 139 | |
a6b5a28e | 140 | out: |
08734048 | 141 | kfree(key); |
a6b5a28e | 142 | return 0; |
08734048 DH |
143 | } |
144 | ||
145 | /* | |
146 | * release a per-superblock cookie | |
147 | */ | |
148 | void nfs_fscache_release_super_cookie(struct super_block *sb) | |
149 | { | |
150 | struct nfs_server *nfss = NFS_SB(sb); | |
151 | ||
a6b5a28e | 152 | fscache_relinquish_volume(nfss->fscache, NULL, false); |
08734048 | 153 | nfss->fscache = NULL; |
a6b5a28e | 154 | kfree(nfss->fscache_uniq); |
50eaa652 DW |
155 | } |
156 | ||
ef79c097 DH |
157 | /* |
158 | * Initialise the per-inode cache cookie pointer for an NFS inode. | |
159 | */ | |
f1fe29b4 | 160 | void nfs_fscache_init_inode(struct inode *inode) |
ef79c097 | 161 | { |
402cb8dd | 162 | struct nfs_fscache_inode_auxdata auxdata; |
dea1bb35 | 163 | struct nfs_server *nfss = NFS_SERVER(inode); |
ef79c097 DH |
164 | struct nfs_inode *nfsi = NFS_I(inode); |
165 | ||
f1fe29b4 | 166 | nfsi->fscache = NULL; |
dea1bb35 | 167 | if (!(nfss->fscache && S_ISREG(inode->i_mode))) |
ef79c097 | 168 | return; |
402cb8dd | 169 | |
45f3a70b | 170 | nfs_fscache_update_auxdata(&auxdata, inode); |
402cb8dd | 171 | |
f1fe29b4 | 172 | nfsi->fscache = fscache_acquire_cookie(NFS_SB(inode->i_sb)->fscache, |
a6b5a28e DW |
173 | 0, |
174 | nfsi->fh.data, /* index_key */ | |
175 | nfsi->fh.size, | |
176 | &auxdata, /* aux_data */ | |
177 | sizeof(auxdata), | |
45f3a70b | 178 | i_size_read(inode)); |
ef79c097 DH |
179 | } |
180 | ||
181 | /* | |
182 | * Release a per-inode cookie. | |
183 | */ | |
f1fe29b4 | 184 | void nfs_fscache_clear_inode(struct inode *inode) |
ef79c097 DH |
185 | { |
186 | struct nfs_inode *nfsi = NFS_I(inode); | |
f1fe29b4 | 187 | struct fscache_cookie *cookie = nfs_i_fscache(inode); |
ef79c097 | 188 | |
a6b5a28e | 189 | fscache_relinquish_cookie(cookie, false); |
ef79c097 DH |
190 | nfsi->fscache = NULL; |
191 | } | |
192 | ||
ef79c097 | 193 | /* |
f1fe29b4 DH |
194 | * Enable or disable caching for a file that is being opened as appropriate. |
195 | * The cookie is allocated when the inode is initialised, but is not enabled at | |
196 | * that time. Enablement is deferred to file-open time to avoid stat() and | |
197 | * access() thrashing the cache. | |
198 | * | |
199 | * For now, with NFS, only regular files that are open read-only will be able | |
200 | * to use the cache. | |
201 | * | |
202 | * We enable the cache for an inode if we open it read-only and it isn't | |
203 | * currently open for writing. We disable the cache if the inode is open | |
204 | * write-only. | |
205 | * | |
206 | * The caller uses the file struct to pin i_writecount on the inode before | |
207 | * calling us when a file is opened for writing, so we can make use of that. | |
208 | * | |
209 | * Note that this may be invoked multiple times in parallel by parallel | |
210 | * nfs_open() functions. | |
ef79c097 | 211 | */ |
f1fe29b4 | 212 | void nfs_fscache_open_file(struct inode *inode, struct file *filp) |
ef79c097 | 213 | { |
402cb8dd | 214 | struct nfs_fscache_inode_auxdata auxdata; |
f1fe29b4 | 215 | struct fscache_cookie *cookie = nfs_i_fscache(inode); |
a6b5a28e | 216 | bool open_for_write = inode_is_open_for_write(inode); |
ef79c097 | 217 | |
f1fe29b4 DH |
218 | if (!fscache_cookie_valid(cookie)) |
219 | return; | |
ef79c097 | 220 | |
a6b5a28e DW |
221 | fscache_use_cookie(cookie, open_for_write); |
222 | if (open_for_write) { | |
45f3a70b | 223 | nfs_fscache_update_auxdata(&auxdata, inode); |
a6b5a28e DW |
224 | fscache_invalidate(cookie, &auxdata, i_size_read(inode), |
225 | FSCACHE_INVAL_DIO_WRITE); | |
ef79c097 | 226 | } |
ef79c097 | 227 | } |
f1fe29b4 | 228 | EXPORT_SYMBOL_GPL(nfs_fscache_open_file); |
545db45f | 229 | |
a6b5a28e | 230 | void nfs_fscache_release_file(struct inode *inode, struct file *filp) |
545db45f | 231 | { |
a6b5a28e | 232 | struct nfs_fscache_inode_auxdata auxdata; |
f1fe29b4 | 233 | struct fscache_cookie *cookie = nfs_i_fscache(inode); |
545db45f | 234 | |
a6b5a28e | 235 | if (fscache_cookie_valid(cookie)) { |
45f3a70b | 236 | nfs_fscache_update_auxdata(&auxdata, inode); |
a6b5a28e DW |
237 | fscache_unuse_cookie(cookie, &auxdata, NULL); |
238 | } | |
9a9fc1c0 DH |
239 | } |
240 | ||
16f2f4e6 DH |
241 | /* |
242 | * Fallback page reading interface. | |
243 | */ | |
244 | static int fscache_fallback_read_page(struct inode *inode, struct page *page) | |
245 | { | |
246 | struct netfs_cache_resources cres; | |
247 | struct fscache_cookie *cookie = nfs_i_fscache(inode); | |
248 | struct iov_iter iter; | |
249 | struct bio_vec bvec[1]; | |
250 | int ret; | |
251 | ||
252 | memset(&cres, 0, sizeof(cres)); | |
253 | bvec[0].bv_page = page; | |
254 | bvec[0].bv_offset = 0; | |
255 | bvec[0].bv_len = PAGE_SIZE; | |
256 | iov_iter_bvec(&iter, READ, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); | |
257 | ||
258 | ret = fscache_begin_read_operation(&cres, cookie); | |
259 | if (ret < 0) | |
260 | return ret; | |
261 | ||
262 | ret = fscache_read(&cres, page_offset(page), &iter, NETFS_READ_HOLE_FAIL, | |
263 | NULL, NULL); | |
264 | fscache_end_operation(&cres); | |
265 | return ret; | |
266 | } | |
267 | ||
268 | /* | |
269 | * Fallback page writing interface. | |
270 | */ | |
271 | static int fscache_fallback_write_page(struct inode *inode, struct page *page, | |
272 | bool no_space_allocated_yet) | |
273 | { | |
274 | struct netfs_cache_resources cres; | |
275 | struct fscache_cookie *cookie = nfs_i_fscache(inode); | |
276 | struct iov_iter iter; | |
277 | struct bio_vec bvec[1]; | |
278 | loff_t start = page_offset(page); | |
279 | size_t len = PAGE_SIZE; | |
280 | int ret; | |
281 | ||
282 | memset(&cres, 0, sizeof(cres)); | |
283 | bvec[0].bv_page = page; | |
284 | bvec[0].bv_offset = 0; | |
285 | bvec[0].bv_len = PAGE_SIZE; | |
286 | iov_iter_bvec(&iter, WRITE, bvec, ARRAY_SIZE(bvec), PAGE_SIZE); | |
287 | ||
288 | ret = fscache_begin_write_operation(&cres, cookie); | |
289 | if (ret < 0) | |
290 | return ret; | |
291 | ||
292 | ret = cres.ops->prepare_write(&cres, &start, &len, i_size_read(inode), | |
293 | no_space_allocated_yet); | |
294 | if (ret == 0) | |
295 | ret = fscache_write(&cres, page_offset(page), &iter, NULL, NULL); | |
296 | fscache_end_operation(&cres); | |
297 | return ret; | |
298 | } | |
299 | ||
9a9fc1c0 DH |
300 | /* |
301 | * Retrieve a page from fscache | |
302 | */ | |
fc1c5abf | 303 | int __nfs_fscache_read_page(struct inode *inode, struct page *page) |
9a9fc1c0 | 304 | { |
16f2f4e6 DH |
305 | int ret; |
306 | ||
e3f0a7fe | 307 | trace_nfs_fscache_read_page(inode, page); |
ba512c1b DW |
308 | if (PageChecked(page)) { |
309 | ClearPageChecked(page); | |
e3f0a7fe DW |
310 | ret = 1; |
311 | goto out; | |
ba512c1b DW |
312 | } |
313 | ||
16f2f4e6 DH |
314 | ret = fscache_fallback_read_page(inode, page); |
315 | if (ret < 0) { | |
316 | nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_FAIL); | |
16f2f4e6 | 317 | SetPageChecked(page); |
e3f0a7fe | 318 | goto out; |
16f2f4e6 | 319 | } |
9a9fc1c0 | 320 | |
16f2f4e6 | 321 | /* Read completed synchronously */ |
16f2f4e6 DH |
322 | nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_READ_OK); |
323 | SetPageUptodate(page); | |
e3f0a7fe DW |
324 | ret = 0; |
325 | out: | |
326 | trace_nfs_fscache_read_page_exit(inode, page, ret); | |
327 | return ret; | |
9a9fc1c0 | 328 | } |
7f8e05f6 DH |
329 | |
330 | /* | |
16f2f4e6 DH |
331 | * Store a newly fetched page in fscache. We can be certain there's no page |
332 | * stored in the cache as yet otherwise we would've read it from there. | |
7f8e05f6 | 333 | */ |
fc1c5abf | 334 | void __nfs_fscache_write_page(struct inode *inode, struct page *page) |
7f8e05f6 | 335 | { |
16f2f4e6 DH |
336 | int ret; |
337 | ||
e3f0a7fe | 338 | trace_nfs_fscache_write_page(inode, page); |
7f8e05f6 | 339 | |
16f2f4e6 DH |
340 | ret = fscache_fallback_write_page(inode, page, true); |
341 | ||
16f2f4e6 DH |
342 | if (ret != 0) { |
343 | nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_FAIL); | |
344 | nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_UNCACHED); | |
345 | } else { | |
346 | nfs_inc_fscache_stats(inode, NFSIOS_FSCACHE_PAGES_WRITTEN_OK); | |
347 | } | |
e3f0a7fe | 348 | trace_nfs_fscache_write_page_exit(inode, page, ret); |
7f8e05f6 | 349 | } |