aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ceph/cache.c
diff options
context:
space:
mode:
authorMilosz Tanski <milosz@adfin.com>2013-08-21 17:29:54 -0400
committerMilosz Tanski <milosz@adfin.com>2013-09-06 12:50:11 -0400
commit99ccbd229cf7453206bc858e795ec1f0345ff258 (patch)
tree343ab082d154d7c970d90bf44c9887fdb0251cdb /fs/ceph/cache.c
parentcd0a2df681ec2af45f50c555c2a39dc92a4dff71 (diff)
ceph: use fscache as a local presisent cache
Adding support for fscache to the Ceph filesystem. This would bring it to on par with some of the other network filesystems in Linux (like NFS, AFS, etc...) In order to mount the filesystem with fscache the 'fsc' mount option must be passed. Signed-off-by: Milosz Tanski <milosz@adfin.com> Signed-off-by: Sage Weil <sage@inktank.com>
Diffstat (limited to 'fs/ceph/cache.c')
-rw-r--r--fs/ceph/cache.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
new file mode 100644
index 000000000000..5c413ecf1f15
--- /dev/null
+++ b/fs/ceph/cache.c
@@ -0,0 +1,393 @@
1/*
2 * Ceph cache definitions.
3 *
4 * Copyright (C) 2013 by Adfin Solutions, Inc. All Rights Reserved.
5 * Written by Milosz Tanski (milosz@adfin.com)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to:
18 * Free Software Foundation
19 * 51 Franklin Street, Fifth Floor
20 * Boston, MA 02111-1301 USA
21 *
22 */
23
24#include <linux/fscache.h>
25
26#include "super.h"
27#include "cache.h"
28
29struct ceph_aux_inode {
30 struct timespec mtime;
31 loff_t size;
32};
33
34struct fscache_netfs ceph_cache_netfs = {
35 .name = "ceph",
36 .version = 0,
37};
38
39static uint16_t ceph_fscache_session_get_key(const void *cookie_netfs_data,
40 void *buffer, uint16_t maxbuf)
41{
42 const struct ceph_fs_client* fsc = cookie_netfs_data;
43 uint16_t klen;
44
45 klen = sizeof(fsc->client->fsid);
46 if (klen > maxbuf)
47 return 0;
48
49 memcpy(buffer, &fsc->client->fsid, klen);
50 return klen;
51}
52
53static const struct fscache_cookie_def ceph_fscache_fsid_object_def = {
54 .name = "CEPH.fsid",
55 .type = FSCACHE_COOKIE_TYPE_INDEX,
56 .get_key = ceph_fscache_session_get_key,
57};
58
59int ceph_fscache_register()
60{
61 return fscache_register_netfs(&ceph_cache_netfs);
62}
63
64void ceph_fscache_unregister()
65{
66 fscache_unregister_netfs(&ceph_cache_netfs);
67}
68
69int ceph_fscache_register_fs(struct ceph_fs_client* fsc)
70{
71 fsc->fscache = fscache_acquire_cookie(ceph_cache_netfs.primary_index,
72 &ceph_fscache_fsid_object_def,
73 fsc);
74
75 if (fsc->fscache == NULL) {
76 pr_err("Unable to resgister fsid: %p fscache cookie", fsc);
77 return 0;
78 }
79
80 fsc->revalidate_wq = alloc_workqueue("ceph-revalidate", 0, 1);
81 if (fsc->revalidate_wq == NULL)
82 return -ENOMEM;
83
84 return 0;
85}
86
87static uint16_t ceph_fscache_inode_get_key(const void *cookie_netfs_data,
88 void *buffer, uint16_t maxbuf)
89{
90 const struct ceph_inode_info* ci = cookie_netfs_data;
91 uint16_t klen;
92
93 /* use ceph virtual inode (id + snaphot) */
94 klen = sizeof(ci->i_vino);
95 if (klen > maxbuf)
96 return 0;
97
98 memcpy(buffer, &ci->i_vino, klen);
99 return klen;
100}
101
102static uint16_t ceph_fscache_inode_get_aux(const void *cookie_netfs_data,
103 void *buffer, uint16_t bufmax)
104{
105 struct ceph_aux_inode aux;
106 const struct ceph_inode_info* ci = cookie_netfs_data;
107 const struct inode* inode = &ci->vfs_inode;
108
109 memset(&aux, 0, sizeof(aux));
110 aux.mtime = inode->i_mtime;
111 aux.size = inode->i_size;
112
113 memcpy(buffer, &aux, sizeof(aux));
114
115 return sizeof(aux);
116}
117
118static void ceph_fscache_inode_get_attr(const void *cookie_netfs_data,
119 uint64_t *size)
120{
121 const struct ceph_inode_info* ci = cookie_netfs_data;
122 const struct inode* inode = &ci->vfs_inode;
123
124 *size = inode->i_size;
125}
126
127static enum fscache_checkaux ceph_fscache_inode_check_aux(
128 void *cookie_netfs_data, const void *data, uint16_t dlen)
129{
130 struct ceph_aux_inode aux;
131 struct ceph_inode_info* ci = cookie_netfs_data;
132 struct inode* inode = &ci->vfs_inode;
133
134 if (dlen != sizeof(aux))
135 return FSCACHE_CHECKAUX_OBSOLETE;
136
137 memset(&aux, 0, sizeof(aux));
138 aux.mtime = inode->i_mtime;
139 aux.size = inode->i_size;
140
141 if (memcmp(data, &aux, sizeof(aux)) != 0)
142 return FSCACHE_CHECKAUX_OBSOLETE;
143
144 dout("ceph inode 0x%p cached okay", ci);
145 return FSCACHE_CHECKAUX_OKAY;
146}
147
148static void ceph_fscache_inode_now_uncached(void* cookie_netfs_data)
149{
150 struct ceph_inode_info* ci = cookie_netfs_data;
151 struct pagevec pvec;
152 pgoff_t first;
153 int loop, nr_pages;
154
155 pagevec_init(&pvec, 0);
156 first = 0;
157
158 dout("ceph inode 0x%p now uncached", ci);
159
160 while (1) {
161 nr_pages = pagevec_lookup(&pvec, ci->vfs_inode.i_mapping, first,
162 PAGEVEC_SIZE - pagevec_count(&pvec));
163
164 if (!nr_pages)
165 break;
166
167 for (loop = 0; loop < nr_pages; loop++)
168 ClearPageFsCache(pvec.pages[loop]);
169
170 first = pvec.pages[nr_pages - 1]->index + 1;
171
172 pvec.nr = nr_pages;
173 pagevec_release(&pvec);
174 cond_resched();
175 }
176}
177
178static const struct fscache_cookie_def ceph_fscache_inode_object_def = {
179 .name = "CEPH.inode",
180 .type = FSCACHE_COOKIE_TYPE_DATAFILE,
181 .get_key = ceph_fscache_inode_get_key,
182 .get_attr = ceph_fscache_inode_get_attr,
183 .get_aux = ceph_fscache_inode_get_aux,
184 .check_aux = ceph_fscache_inode_check_aux,
185 .now_uncached = ceph_fscache_inode_now_uncached,
186};
187
188void ceph_fscache_register_inode_cookie(struct ceph_fs_client* fsc,
189 struct ceph_inode_info* ci)
190{
191 struct inode* inode = &ci->vfs_inode;
192
193 /* No caching for filesystem */
194 if (fsc->fscache == NULL)
195 return;
196
197 /* Only cache for regular files that are read only */
198 if ((ci->vfs_inode.i_mode & S_IFREG) == 0)
199 return;
200
201 /* Avoid multiple racing open requests */
202 mutex_lock(&inode->i_mutex);
203
204 if (ci->fscache)
205 goto done;
206
207 ci->fscache = fscache_acquire_cookie(fsc->fscache,
208 &ceph_fscache_inode_object_def,
209 ci);
210done:
211 mutex_unlock(&inode->i_mutex);
212
213}
214
215void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info* ci)
216{
217 struct fscache_cookie* cookie;
218
219 if ((cookie = ci->fscache) == NULL)
220 return;
221
222 ci->fscache = NULL;
223
224 fscache_uncache_all_inode_pages(cookie, &ci->vfs_inode);
225 fscache_relinquish_cookie(cookie, 0);
226}
227
228static void ceph_vfs_readpage_complete(struct page *page, void *data, int error)
229{
230 if (!error)
231 SetPageUptodate(page);
232}
233
234static void ceph_vfs_readpage_complete_unlock(struct page *page, void *data, int error)
235{
236 if (!error)
237 SetPageUptodate(page);
238
239 unlock_page(page);
240}
241
242static inline int cache_valid(struct ceph_inode_info *ci)
243{
244 return ((ceph_caps_issued(ci) & CEPH_CAP_FILE_CACHE) &&
245 (ci->i_fscache_gen == ci->i_rdcache_gen));
246}
247
248
249/* Atempt to read from the fscache,
250 *
251 * This function is called from the readpage_nounlock context. DO NOT attempt to
252 * unlock the page here (or in the callback).
253 */
254int ceph_readpage_from_fscache(struct inode *inode, struct page *page)
255{
256 struct ceph_inode_info *ci = ceph_inode(inode);
257 int ret;
258
259 if (!cache_valid(ci))
260 return -ENOBUFS;
261
262 ret = fscache_read_or_alloc_page(ci->fscache, page,
263 ceph_vfs_readpage_complete, NULL,
264 GFP_KERNEL);
265
266 switch (ret) {
267 case 0: /* Page found */
268 dout("page read submitted\n");
269 return 0;
270 case -ENOBUFS: /* Pages were not found, and can't be */
271 case -ENODATA: /* Pages were not found */
272 dout("page/inode not in cache\n");
273 return ret;
274 default:
275 dout("%s: unknown error ret = %i\n", __func__, ret);
276 return ret;
277 }
278}
279
280int ceph_readpages_from_fscache(struct inode *inode,
281 struct address_space *mapping,
282 struct list_head *pages,
283 unsigned *nr_pages)
284{
285 struct ceph_inode_info *ci = ceph_inode(inode);
286 int ret;
287
288 if (!cache_valid(ci))
289 return -ENOBUFS;
290
291 ret = fscache_read_or_alloc_pages(ci->fscache, mapping, pages, nr_pages,
292 ceph_vfs_readpage_complete_unlock,
293 NULL, mapping_gfp_mask(mapping));
294
295 switch (ret) {
296 case 0: /* All pages found */
297 dout("all-page read submitted\n");
298 return 0;
299 case -ENOBUFS: /* Some pages were not found, and can't be */
300 case -ENODATA: /* some pages were not found */
301 dout("page/inode not in cache\n");
302 return ret;
303 default:
304 dout("%s: unknown error ret = %i\n", __func__, ret);
305 return ret;
306 }
307}
308
309void ceph_readpage_to_fscache(struct inode *inode, struct page *page)
310{
311 struct ceph_inode_info *ci = ceph_inode(inode);
312 int ret;
313
314 if (!cache_valid(ci))
315 return;
316
317 ret = fscache_write_page(ci->fscache, page, GFP_KERNEL);
318 if (ret)
319 fscache_uncache_page(ci->fscache, page);
320}
321
322void ceph_invalidate_fscache_page(struct inode* inode, struct page *page)
323{
324 struct ceph_inode_info *ci = ceph_inode(inode);
325
326 fscache_wait_on_page_write(ci->fscache, page);
327 fscache_uncache_page(ci->fscache, page);
328}
329
330void ceph_fscache_unregister_fs(struct ceph_fs_client* fsc)
331{
332 if (fsc->revalidate_wq)
333 destroy_workqueue(fsc->revalidate_wq);
334
335 fscache_relinquish_cookie(fsc->fscache, 0);
336 fsc->fscache = NULL;
337}
338
339static void ceph_revalidate_work(struct work_struct *work)
340{
341 int issued;
342 u32 orig_gen;
343 struct ceph_inode_info *ci = container_of(work, struct ceph_inode_info,
344 i_revalidate_work);
345 struct inode *inode = &ci->vfs_inode;
346
347 spin_lock(&ci->i_ceph_lock);
348 issued = __ceph_caps_issued(ci, NULL);
349 orig_gen = ci->i_rdcache_gen;
350 spin_unlock(&ci->i_ceph_lock);
351
352 if (!(issued & CEPH_CAP_FILE_CACHE)) {
353 dout("revalidate_work lost cache before validation %p\n",
354 inode);
355 goto out;
356 }
357
358 if (!fscache_check_consistency(ci->fscache))
359 fscache_invalidate(ci->fscache);
360
361 spin_lock(&ci->i_ceph_lock);
362 /* Update the new valid generation (backwards sanity check too) */
363 if (orig_gen > ci->i_fscache_gen) {
364 ci->i_fscache_gen = orig_gen;
365 }
366 spin_unlock(&ci->i_ceph_lock);
367
368out:
369 iput(&ci->vfs_inode);
370}
371
372void ceph_queue_revalidate(struct inode *inode)
373{
374 struct ceph_inode_info *ci = ceph_inode(inode);
375
376 ihold(inode);
377
378 if (queue_work(ceph_sb_to_client(inode->i_sb)->revalidate_wq,
379 &ci->i_revalidate_work)) {
380 dout("ceph_queue_revalidate %p\n", inode);
381 } else {
382 dout("ceph_queue_revalidate %p failed\n)", inode);
383 iput(inode);
384 }
385}
386
387void ceph_fscache_inode_init(struct ceph_inode_info *ci)
388{
389 ci->fscache = NULL;
390 /* The first load is verifed cookie open time */
391 ci->i_fscache_gen = 1;
392 INIT_WORK(&ci->i_revalidate_work, ceph_revalidate_work);
393}