blob: 01df30d256b8d56874d7cfa900b1e0c064eaa026 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* file.c: AFS filesystem file handling
2 *
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * 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 "volume.h"
19#include "vnode.h"
20#include <rxrpc/call.h>
21#include "internal.h"
22
23#if 0
24static int afs_file_open(struct inode *inode, struct file *file);
25static int afs_file_release(struct inode *inode, struct file *file);
26#endif
27
28static int afs_file_readpage(struct file *file, struct page *page);
NeilBrown2ff28e22006-03-26 01:37:18 -080029static void afs_file_invalidatepage(struct page *page, unsigned long offset);
Al Viro27496a82005-10-21 03:20:48 -040030static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
Arjan van de Ven754661f2007-02-12 00:55:38 -080032const struct inode_operations afs_file_inode_operations = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 .getattr = afs_inode_getattr,
34};
35
Christoph Hellwigf5e54d62006-06-28 04:26:44 -070036const struct address_space_operations afs_fs_aops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070037 .readpage = afs_file_readpage,
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 .set_page_dirty = __set_page_dirty_nobuffers,
39 .releasepage = afs_file_releasepage,
40 .invalidatepage = afs_file_invalidatepage,
41};
42
Linus Torvalds1da177e2005-04-16 15:20:36 -070043/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 * deal with notification that a page was read from the cache
45 */
46#ifdef AFS_CACHING_SUPPORT
47static void afs_file_readpage_read_complete(void *cookie_data,
48 struct page *page,
49 void *data,
50 int error)
51{
52 _enter("%p,%p,%p,%d", cookie_data, page, data, error);
53
54 if (error)
55 SetPageError(page);
56 else
57 SetPageUptodate(page);
58 unlock_page(page);
59
David Howellsec268152007-04-26 15:49:28 -070060}
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#endif
62
Linus Torvalds1da177e2005-04-16 15:20:36 -070063/*
64 * deal with notification that a page was written to the cache
65 */
66#ifdef AFS_CACHING_SUPPORT
67static void afs_file_readpage_write_complete(void *cookie_data,
68 struct page *page,
69 void *data,
70 int error)
71{
72 _enter("%p,%p,%p,%d", cookie_data, page, data, error);
73
74 unlock_page(page);
David Howellsec268152007-04-26 15:49:28 -070075}
Linus Torvalds1da177e2005-04-16 15:20:36 -070076#endif
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078/*
79 * AFS read page from file (or symlink)
80 */
81static int afs_file_readpage(struct file *file, struct page *page)
82{
83 struct afs_rxfs_fetch_descriptor desc;
84#ifdef AFS_CACHING_SUPPORT
85 struct cachefs_page *pageio;
86#endif
87 struct afs_vnode *vnode;
88 struct inode *inode;
89 int ret;
90
91 inode = page->mapping->host;
92
93 _enter("{%lu},{%lu}", inode->i_ino, page->index);
94
95 vnode = AFS_FS_I(inode);
96
Matt Mackallcd7619d2005-05-01 08:59:01 -070097 BUG_ON(!PageLocked(page));
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
99 ret = -ESTALE;
100 if (vnode->flags & AFS_VNODE_DELETED)
101 goto error;
102
103#ifdef AFS_CACHING_SUPPORT
104 ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
105 if (ret < 0)
106 goto error;
107
108 /* is it cached? */
109 ret = cachefs_read_or_alloc_page(vnode->cache,
110 page,
111 afs_file_readpage_read_complete,
112 NULL,
113 GFP_KERNEL);
114#else
115 ret = -ENOBUFS;
116#endif
117
118 switch (ret) {
119 /* read BIO submitted and wb-journal entry found */
120 case 1:
121 BUG(); // TODO - handle wb-journal match
122
123 /* read BIO submitted (page in cache) */
124 case 0:
125 break;
126
127 /* no page available in cache */
128 case -ENOBUFS:
129 case -ENODATA:
130 default:
131 desc.fid = vnode->fid;
132 desc.offset = page->index << PAGE_CACHE_SHIFT;
133 desc.size = min((size_t) (inode->i_size - desc.offset),
134 (size_t) PAGE_SIZE);
135 desc.buffer = kmap(page);
136
137 clear_page(desc.buffer);
138
139 /* read the contents of the file from the server into the
140 * page */
141 ret = afs_vnode_fetch_data(vnode, &desc);
142 kunmap(page);
143 if (ret < 0) {
144 if (ret==-ENOENT) {
145 _debug("got NOENT from server"
146 " - marking file deleted and stale");
147 vnode->flags |= AFS_VNODE_DELETED;
148 ret = -ESTALE;
149 }
150
151#ifdef AFS_CACHING_SUPPORT
152 cachefs_uncache_page(vnode->cache, page);
153#endif
154 goto error;
155 }
156
157 SetPageUptodate(page);
158
159#ifdef AFS_CACHING_SUPPORT
160 if (cachefs_write_page(vnode->cache,
161 page,
162 afs_file_readpage_write_complete,
163 NULL,
164 GFP_KERNEL) != 0
165 ) {
166 cachefs_uncache_page(vnode->cache, page);
167 unlock_page(page);
168 }
169#else
170 unlock_page(page);
171#endif
172 }
173
174 _leave(" = 0");
175 return 0;
176
177 error:
178 SetPageError(page);
179 unlock_page(page);
180
181 _leave(" = %d", ret);
182 return ret;
David Howellsec268152007-04-26 15:49:28 -0700183}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185/*
186 * get a page cookie for the specified page
187 */
188#ifdef AFS_CACHING_SUPPORT
189int afs_cache_get_page_cookie(struct page *page,
190 struct cachefs_page **_page_cookie)
191{
192 int ret;
193
194 _enter("");
195 ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO);
196
197 _leave(" = %d", ret);
198 return ret;
David Howellsec268152007-04-26 15:49:28 -0700199}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200#endif
201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202/*
203 * invalidate part or all of a page
204 */
NeilBrown2ff28e22006-03-26 01:37:18 -0800205static void afs_file_invalidatepage(struct page *page, unsigned long offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 int ret = 1;
208
209 _enter("{%lu},%lu", page->index, offset);
210
211 BUG_ON(!PageLocked(page));
212
213 if (PagePrivate(page)) {
214#ifdef AFS_CACHING_SUPPORT
215 struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
216 cachefs_uncache_page(vnode->cache,page);
217#endif
218
219 /* We release buffers only if the entire page is being
220 * invalidated.
221 * The get_block cached value has been unconditionally
222 * invalidated, so real IO is not possible anymore.
223 */
224 if (offset == 0) {
225 BUG_ON(!PageLocked(page));
226
227 ret = 0;
228 if (!PageWriteback(page))
229 ret = page->mapping->a_ops->releasepage(page,
230 0);
NeilBrown2ff28e22006-03-26 01:37:18 -0800231 /* possibly should BUG_ON(!ret); - neilb */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 }
233 }
234
235 _leave(" = %d", ret);
David Howellsec268152007-04-26 15:49:28 -0700236}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238/*
239 * release a page and cleanup its private data
240 */
Al Viro27496a82005-10-21 03:20:48 -0400241static int afs_file_releasepage(struct page *page, gfp_t gfp_flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 struct cachefs_page *pageio;
244
245 _enter("{%lu},%x", page->index, gfp_flags);
246
247 if (PagePrivate(page)) {
248#ifdef AFS_CACHING_SUPPORT
249 struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
250 cachefs_uncache_page(vnode->cache, page);
251#endif
252
Hugh Dickins4c21e2f2005-10-29 18:16:40 -0700253 pageio = (struct cachefs_page *) page_private(page);
254 set_page_private(page, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 ClearPagePrivate(page);
256
Jesper Juhlf99d49a2005-11-07 01:01:34 -0800257 kfree(pageio);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 }
259
260 _leave(" = 0");
261 return 0;
David Howellsec268152007-04-26 15:49:28 -0700262}