diff options
Diffstat (limited to 'fs/ceph/cache.c')
-rw-r--r-- | fs/ceph/cache.c | 393 |
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 | |||
29 | struct ceph_aux_inode { | ||
30 | struct timespec mtime; | ||
31 | loff_t size; | ||
32 | }; | ||
33 | |||
34 | struct fscache_netfs ceph_cache_netfs = { | ||
35 | .name = "ceph", | ||
36 | .version = 0, | ||
37 | }; | ||
38 | |||
39 | static 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 | |||
53 | static 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 | |||
59 | int ceph_fscache_register() | ||
60 | { | ||
61 | return fscache_register_netfs(&ceph_cache_netfs); | ||
62 | } | ||
63 | |||
64 | void ceph_fscache_unregister() | ||
65 | { | ||
66 | fscache_unregister_netfs(&ceph_cache_netfs); | ||
67 | } | ||
68 | |||
69 | int 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 | |||
87 | static 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 | |||
102 | static 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 | |||
118 | static 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 | |||
127 | static 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 | |||
148 | static 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 | |||
178 | static 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 | |||
188 | void 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); | ||
210 | done: | ||
211 | mutex_unlock(&inode->i_mutex); | ||
212 | |||
213 | } | ||
214 | |||
215 | void 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 | |||
228 | static void ceph_vfs_readpage_complete(struct page *page, void *data, int error) | ||
229 | { | ||
230 | if (!error) | ||
231 | SetPageUptodate(page); | ||
232 | } | ||
233 | |||
234 | static 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 | |||
242 | static 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 | */ | ||
254 | int 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 | |||
280 | int 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 | |||
309 | void 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 | |||
322 | void 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 | |||
330 | void 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 | |||
339 | static 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 | |||
368 | out: | ||
369 | iput(&ci->vfs_inode); | ||
370 | } | ||
371 | |||
372 | void 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 | |||
387 | void 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 | } | ||