xref: /openbmc/linux/fs/afs/file.c (revision 87c2ce3b)
1 /* 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>
15 #include <linux/sched.h>
16 #include <linux/slab.h>
17 #include <linux/fs.h>
18 #include <linux/pagemap.h>
19 #include <linux/buffer_head.h>
20 #include "volume.h"
21 #include "vnode.h"
22 #include <rxrpc/call.h>
23 #include "internal.h"
24 
25 #if 0
26 static int afs_file_open(struct inode *inode, struct file *file);
27 static int afs_file_release(struct inode *inode, struct file *file);
28 #endif
29 
30 static int afs_file_readpage(struct file *file, struct page *page);
31 static int afs_file_invalidatepage(struct page *page, unsigned long offset);
32 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags);
33 
34 struct inode_operations afs_file_inode_operations = {
35 	.getattr	= afs_inode_getattr,
36 };
37 
38 struct address_space_operations afs_fs_aops = {
39 	.readpage	= afs_file_readpage,
40 	.sync_page	= block_sync_page,
41 	.set_page_dirty	= __set_page_dirty_nobuffers,
42 	.releasepage	= afs_file_releasepage,
43 	.invalidatepage	= afs_file_invalidatepage,
44 };
45 
46 /*****************************************************************************/
47 /*
48  * deal with notification that a page was read from the cache
49  */
50 #ifdef AFS_CACHING_SUPPORT
51 static void afs_file_readpage_read_complete(void *cookie_data,
52 					    struct page *page,
53 					    void *data,
54 					    int error)
55 {
56 	_enter("%p,%p,%p,%d", cookie_data, page, data, error);
57 
58 	if (error)
59 		SetPageError(page);
60 	else
61 		SetPageUptodate(page);
62 	unlock_page(page);
63 
64 } /* end afs_file_readpage_read_complete() */
65 #endif
66 
67 /*****************************************************************************/
68 /*
69  * deal with notification that a page was written to the cache
70  */
71 #ifdef AFS_CACHING_SUPPORT
72 static void afs_file_readpage_write_complete(void *cookie_data,
73 					     struct page *page,
74 					     void *data,
75 					     int error)
76 {
77 	_enter("%p,%p,%p,%d", cookie_data, page, data, error);
78 
79 	unlock_page(page);
80 
81 } /* end afs_file_readpage_write_complete() */
82 #endif
83 
84 /*****************************************************************************/
85 /*
86  * AFS read page from file (or symlink)
87  */
88 static int afs_file_readpage(struct file *file, struct page *page)
89 {
90 	struct afs_rxfs_fetch_descriptor desc;
91 #ifdef AFS_CACHING_SUPPORT
92 	struct cachefs_page *pageio;
93 #endif
94 	struct afs_vnode *vnode;
95 	struct inode *inode;
96 	int ret;
97 
98 	inode = page->mapping->host;
99 
100 	_enter("{%lu},{%lu}", inode->i_ino, page->index);
101 
102 	vnode = AFS_FS_I(inode);
103 
104 	BUG_ON(!PageLocked(page));
105 
106 	ret = -ESTALE;
107 	if (vnode->flags & AFS_VNODE_DELETED)
108 		goto error;
109 
110 #ifdef AFS_CACHING_SUPPORT
111 	ret = cachefs_page_get_private(page, &pageio, GFP_NOIO);
112 	if (ret < 0)
113 		goto error;
114 
115 	/* is it cached? */
116 	ret = cachefs_read_or_alloc_page(vnode->cache,
117 					 page,
118 					 afs_file_readpage_read_complete,
119 					 NULL,
120 					 GFP_KERNEL);
121 #else
122 	ret = -ENOBUFS;
123 #endif
124 
125 	switch (ret) {
126 		/* read BIO submitted and wb-journal entry found */
127 	case 1:
128 		BUG(); // TODO - handle wb-journal match
129 
130 		/* read BIO submitted (page in cache) */
131 	case 0:
132 		break;
133 
134 		/* no page available in cache */
135 	case -ENOBUFS:
136 	case -ENODATA:
137 	default:
138 		desc.fid	= vnode->fid;
139 		desc.offset	= page->index << PAGE_CACHE_SHIFT;
140 		desc.size	= min((size_t) (inode->i_size - desc.offset),
141 				      (size_t) PAGE_SIZE);
142 		desc.buffer	= kmap(page);
143 
144 		clear_page(desc.buffer);
145 
146 		/* read the contents of the file from the server into the
147 		 * page */
148 		ret = afs_vnode_fetch_data(vnode, &desc);
149 		kunmap(page);
150 		if (ret < 0) {
151 			if (ret==-ENOENT) {
152 				_debug("got NOENT from server"
153 				       " - marking file deleted and stale");
154 				vnode->flags |= AFS_VNODE_DELETED;
155 				ret = -ESTALE;
156 			}
157 
158 #ifdef AFS_CACHING_SUPPORT
159 			cachefs_uncache_page(vnode->cache, page);
160 #endif
161 			goto error;
162 		}
163 
164 		SetPageUptodate(page);
165 
166 #ifdef AFS_CACHING_SUPPORT
167 		if (cachefs_write_page(vnode->cache,
168 				       page,
169 				       afs_file_readpage_write_complete,
170 				       NULL,
171 				       GFP_KERNEL) != 0
172 		    ) {
173 			cachefs_uncache_page(vnode->cache, page);
174 			unlock_page(page);
175 		}
176 #else
177 		unlock_page(page);
178 #endif
179 	}
180 
181 	_leave(" = 0");
182 	return 0;
183 
184  error:
185 	SetPageError(page);
186 	unlock_page(page);
187 
188 	_leave(" = %d", ret);
189 	return ret;
190 
191 } /* end afs_file_readpage() */
192 
193 /*****************************************************************************/
194 /*
195  * get a page cookie for the specified page
196  */
197 #ifdef AFS_CACHING_SUPPORT
198 int afs_cache_get_page_cookie(struct page *page,
199 			      struct cachefs_page **_page_cookie)
200 {
201 	int ret;
202 
203 	_enter("");
204 	ret = cachefs_page_get_private(page,_page_cookie, GFP_NOIO);
205 
206 	_leave(" = %d", ret);
207 	return ret;
208 } /* end afs_cache_get_page_cookie() */
209 #endif
210 
211 /*****************************************************************************/
212 /*
213  * invalidate part or all of a page
214  */
215 static int afs_file_invalidatepage(struct page *page, unsigned long offset)
216 {
217 	int ret = 1;
218 
219 	_enter("{%lu},%lu", page->index, offset);
220 
221 	BUG_ON(!PageLocked(page));
222 
223 	if (PagePrivate(page)) {
224 #ifdef AFS_CACHING_SUPPORT
225 		struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
226 		cachefs_uncache_page(vnode->cache,page);
227 #endif
228 
229 		/* We release buffers only if the entire page is being
230 		 * invalidated.
231 		 * The get_block cached value has been unconditionally
232 		 * invalidated, so real IO is not possible anymore.
233 		 */
234 		if (offset == 0) {
235 			BUG_ON(!PageLocked(page));
236 
237 			ret = 0;
238 			if (!PageWriteback(page))
239 				ret = page->mapping->a_ops->releasepage(page,
240 									0);
241 		}
242 	}
243 
244 	_leave(" = %d", ret);
245 	return ret;
246 } /* end afs_file_invalidatepage() */
247 
248 /*****************************************************************************/
249 /*
250  * release a page and cleanup its private data
251  */
252 static int afs_file_releasepage(struct page *page, gfp_t gfp_flags)
253 {
254 	struct cachefs_page *pageio;
255 
256 	_enter("{%lu},%x", page->index, gfp_flags);
257 
258 	if (PagePrivate(page)) {
259 #ifdef AFS_CACHING_SUPPORT
260 		struct afs_vnode *vnode = AFS_FS_I(page->mapping->host);
261 		cachefs_uncache_page(vnode->cache, page);
262 #endif
263 
264 		pageio = (struct cachefs_page *) page_private(page);
265 		set_page_private(page, 0);
266 		ClearPagePrivate(page);
267 
268 		kfree(pageio);
269 	}
270 
271 	_leave(" = 0");
272 	return 0;
273 } /* end afs_file_releasepage() */
274