blob: 6990327e75ddcf2fc827b4ceaaf9117e952b3ff2 [file] [log] [blame]
David Howells08e0e7c2007-04-26 15:55:03 -07001/* AFS filesystem file handling
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
David Howells08e0e7c2007-04-26 15:55:03 -07003 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/slab.h>
16#include <linux/fs.h>
17#include <linux/pagemap.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "internal.h"
19
20#if 0
21static int afs_file_open(struct inode *inode, struct file *file);
22static int afs_file_release(struct inode *inode, struct file *file);
23#endif
24
25static int afs_file_readpage(struct file *file, struct page *page);
NeilBrown2ff28e22006-03-26 01:37:18 -080026static void afs_file_invalidatepage(struct page *page, unsigned long offset);
Al Viro27496a82005-10-21 03:20:48 -040027static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Arjan van de Ven754661f2007-02-12 00:55:38 -080029const struct inode_operations afs_file_inode_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070030 .getattr = afs_inode_getattr,
31};
32
Christoph Hellwigf5e54d62006-06-28 04:26:44 -070033const struct address_space_operations afs_fs_aops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070034 .readpage = afs_file_readpage,
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 .set_page_dirty = __set_page_dirty_nobuffers,
36 .releasepage = afs_file_releasepage,
37 .invalidatepage = afs_file_invalidatepage,
38};
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070041 * deal with notification that a page was read from the cache
42 */
43#ifdef AFS_CACHING_SUPPORT
44static void afs_file_readpage_read_complete(void *cookie_data,
45 struct page *page,
46 void *data,
47 int error)
48{
49 _enter("%p,%p,%p,%d", cookie_data, page, data, error);
50
51 if (error)
52 SetPageError(page);
53 else
54 SetPageUptodate(page);
55 unlock_page(page);
56
David Howellsec268152007-04-26 15:49:28 -070057}
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#endif
59
Linus Torvalds1da177e2005-04-16 15:20:36 -070060/*
61 * deal with notification that a page was written to the cache
62 */
63#ifdef AFS_CACHING_SUPPORT
64static void afs_file_readpage_write_complete(void *cookie_data,
65 struct page *page,
66 void *data,
67 int error)
68{
69 _enter("%p,%p,%p,%d", cookie_data, page, data, error);
70
71 unlock_page(page);
David Howellsec268152007-04-26 15:49:28 -070072}
Linus Torvalds1da177e2005-04-16 15:20:36 -070073#endif
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075/*
76 * AFS read page from file (or symlink)
77 */
78static int afs_file_readpage(struct file *file, struct page *page)
79{
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 struct afs_vnode *vnode;
81 struct inode *inode;
David Howells08e0e7c2007-04-26 15:55:03 -070082 size_t len;
83 off_t offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 int ret;
85
86 inode = page->mapping->host;
87
88 _enter("{%lu},{%lu}", inode->i_ino, page->index);
89
90 vnode = AFS_FS_I(inode);
91
Matt Mackallcd7619d2005-05-01 08:59:01 -070092 BUG_ON(!PageLocked(page));
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
94 ret = -ESTALE;
David Howells08e0e7c2007-04-26 15:55:03 -070095 if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 goto error;
97
98#ifdef AFS_CACHING_SUPPORT
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 /* is it cached? */
100 ret = cachefs_read_or_alloc_page(vnode->cache,
101 page,
102 afs_file_readpage_read_complete,
103 NULL,
104 GFP_KERNEL);
105#else
106 ret = -ENOBUFS;
107#endif
108
109 switch (ret) {
110 /* read BIO submitted and wb-journal entry found */
111 case 1:
112 BUG(); // TODO - handle wb-journal match
113
114 /* read BIO submitted (page in cache) */
115 case 0:
116 break;
117
118 /* no page available in cache */
119 case -ENOBUFS:
120 case -ENODATA:
121 default:
David Howells08e0e7c2007-04-26 15:55:03 -0700122 offset = page->index << PAGE_CACHE_SHIFT;
123 len = min_t(size_t, i_size_read(inode) - offset, PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
125 /* read the contents of the file from the server into the
126 * page */
David Howells08e0e7c2007-04-26 15:55:03 -0700127 ret = afs_vnode_fetch_data(vnode, offset, len, page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 if (ret < 0) {
David Howells08e0e7c2007-04-26 15:55:03 -0700129 if (ret == -ENOENT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 _debug("got NOENT from server"
131 " - marking file deleted and stale");
David Howells08e0e7c2007-04-26 15:55:03 -0700132 set_bit(AFS_VNODE_DELETED, &vnode->flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 ret = -ESTALE;
134 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135#ifdef AFS_CACHING_SUPPORT
136 cachefs_uncache_page(vnode->cache, page);
137#endif
138 goto error;
139 }
140
141 SetPageUptodate(page);
142
143#ifdef AFS_CACHING_SUPPORT
144 if (cachefs_write_page(vnode->cache,
145 page,
146 afs_file_readpage_write_complete,
147 NULL,
148 GFP_KERNEL) != 0
149 ) {
150 cachefs_uncache_page(vnode->cache, page);
151 unlock_page(page);
152 }
153#else
154 unlock_page(page);
155#endif
156 }
157
158 _leave(" = 0");
159 return 0;
160
David Howells08e0e7c2007-04-26 15:55:03 -0700161error:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 SetPageError(page);
163 unlock_page(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 _leave(" = %d", ret);
165 return ret;
David Howellsec268152007-04-26 15:49:28 -0700166}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168/*
169 * get a page cookie for the specified page
170 */
171#ifdef AFS_CACHING_SUPPORT
172int afs_cache_get_page_cookie(struct page *page,
173 struct cachefs_page **_page_cookie)
174{
175 int ret;
176
177 _enter("");
178 ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO);
179
180 _leave(" = %d", ret);
181 return ret;
David Howellsec268152007-04-26 15:49:28 -0700182}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#endif
184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185/*
186 * invalidate part or all of a page
187 */
NeilBrown2ff28e22006-03-26 01:37:18 -0800188static void afs_file_invalidatepage(struct page *page, unsigned long offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 int ret = 1;
191
192 _enter("{%lu},%lu", page->index, offset);
193
194 BUG_ON(!PageLocked(page));
195
196 if (PagePrivate(page)) {
197#ifdef AFS_CACHING_SUPPORT
198 struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
199 cachefs_uncache_page(vnode->cache,page);
200#endif
201
202 /* We release buffers only if the entire page is being
203 * invalidated.
204 * The get_block cached value has been unconditionally
205 * invalidated, so real IO is not possible anymore.
206 */
207 if (offset == 0) {
208 BUG_ON(!PageLocked(page));
209
210 ret = 0;
211 if (!PageWriteback(page))
212 ret = page->mapping->a_ops->releasepage(page,
213 0);
NeilBrown2ff28e22006-03-26 01:37:18 -0800214 /* possibly should BUG_ON(!ret); - neilb */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 }
216 }
217
218 _leave(" = %d", ret);
David Howellsec268152007-04-26 15:49:28 -0700219}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221/*
222 * release a page and cleanup its private data
223 */
Al Viro27496a82005-10-21 03:20:48 -0400224static int afs_file_releasepage(struct page *page, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 struct cachefs_page *pageio;
227
228 _enter("{%lu},%x", page->index, gfp_flags);
229
230 if (PagePrivate(page)) {
231#ifdef AFS_CACHING_SUPPORT
232 struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
233 cachefs_uncache_page(vnode->cache, page);
234#endif
235
Hugh Dickins4c21e2f2005-10-29 18:16:40 -0700236 pageio = (struct cachefs_page *) page_private(page);
237 set_page_private(page, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 ClearPagePrivate(page);
239
Jesper Juhlf99d49a2005-11-07 01:01:34 -0800240 kfree(pageio);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 }
242
243 _leave(" = 0");
244 return 0;
David Howellsec268152007-04-26 15:49:28 -0700245}