Whamcloud - gitweb
Working on add_to_page_cache oops.
[fs/lustre-release.git] / lustre / obdfs / rw.c
1 /*
2  * OBDFS Super operations
3  *
4  * Copyright (C) 1996, 1997, Olaf Kirch <okir@monad.swb.de>
5  * Copryright (C) 1999 Stelias Computing Inc, 
6  *                (author Peter J. Braam <braam@stelias.com>)
7  * Copryright (C) 1999 Seagate Technology Inc.
8  */
9
10
11 #include <linux/config.h>
12 #include <linux/kernel.h>
13 #include <linux/mm.h>
14 #include <linux/string.h>
15 #include <linux/stat.h>
16 #include <linux/errno.h>
17 #include <linux/locks.h>
18 #include <linux/unistd.h>
19
20 #include <asm/system.h>
21 #include <asm/uaccess.h>
22
23 #include <linux/fs.h>
24 #include <linux/stat.h>
25 #include <asm/uaccess.h>
26 #include <linux/vmalloc.h>
27 #include <asm/segment.h>
28 #include <linux/mm.h>
29 #include <linux/pagemap.h>
30 #include <linux/smp_lock.h>
31
32 #include <linux/obd_support.h>
33 #include <linux/obd_ext2.h>
34 #include <linux/obdfs.h>
35
36 int console_loglevel;
37
38 /* SYNCHRONOUS I/O for an inode */
39 int obdfs_brw(int rw, struct inode *inode, struct page *page, int create)
40 {
41         struct obdo *obdo;
42         int res;
43
44         obdo = obdo_alloc();
45         if ( ! obdo ) {
46                 EXIT;
47                 return -ENOMEM;
48         }
49
50         obdo->o_id = inode->i_ino;
51
52         res = IOPS(inode, brw)(rw, IID(inode), obdo, 
53                                (char *)page_address(page), 
54                                PAGE_SIZE,
55                                (page->index) >> PAGE_SHIFT,
56                                create);
57
58         obdo_to_inode(inode, obdo); /* copy o_blocks to i_blocks */
59         obdo_free(obdo);
60         
61         if ( res == PAGE_SIZE )
62                 res = 0;
63         return res;
64 }
65
66 /* returns the page unlocked, but with a reference */
67 int obdfs_readpage(struct dentry *dentry, struct page *page)
68 {
69         struct inode *inode = dentry->d_inode;
70         int rc;
71
72         ENTRY;
73         PDEBUG(page, "READ");
74         rc =  obdfs_brw(READ, inode, page, 0);
75         if (!rc) {
76                 SetPageUptodate(page);
77                 UnlockPage(page);
78         } 
79         PDEBUG(page, "READ");
80         EXIT;
81         return rc;
82 }
83
84 static kmem_cache_t *obdfs_wreq_cachep;
85
86 int obdfs_init_wreqcache(void)
87 {
88         /* XXX need to free this somewhere? */
89         ENTRY;
90         obdfs_wreq_cachep = kmem_cache_create("obdfs_wreq",
91                                               sizeof(struct obdfs_wreq),
92                                               0, SLAB_HWCACHE_ALIGN,
93                                               NULL, NULL);
94         if (obdfs_wreq_cachep == NULL) {
95                 EXIT;
96                 return -ENOMEM;
97         }
98         EXIT;
99         return 0;
100 }
101
102 void obdfs_cleanup_wreqcache(void)
103 {
104         if (obdfs_wreq_cachep != NULL)
105                 kmem_cache_destroy(obdfs_wreq_cachep);
106         
107         obdfs_wreq_cachep = NULL;
108 }
109
110
111 /*
112  * Find a specific page in the page cache.  If it is found, we return
113  * the write request struct associated with it, if not found return NULL.
114  */
115 static struct obdfs_wreq *
116 obdfs_find_in_page_cache(struct inode *inode, struct page *page)
117 {
118         struct list_head *list_head = &OBD_LIST(inode);
119         struct obdfs_wreq *head, *wreq;
120
121         ENTRY;
122         CDEBUG(D_INODE, "looking for inode %ld page %p\n", inode->i_ino, page);
123         if (list_empty(list_head)) {
124                 CDEBUG(D_INODE, "empty list\n");
125                 EXIT;
126                 return NULL;
127         }
128         wreq = head = WREQ(list_head->next);
129         do {
130                 CDEBUG(D_INODE, "checking page %p\n", wreq->wb_page);
131                 if (wreq->wb_page == page) {
132                         CDEBUG(D_INODE, "found page %p in list\n", page);
133                         EXIT;
134                         return wreq;
135                 }
136         } while ((wreq = WB_NEXT(wreq)) != head);
137
138         EXIT;
139         return NULL;
140 }
141
142
143 /*
144  * Remove a writeback request from a list
145  */
146 static inline int
147 obdfs_remove_from_page_cache(struct obdfs_wreq *wreq)
148 {
149         struct inode *inode = wreq->wb_inode;
150         struct page *page = wreq->wb_page;
151         int rc;
152
153         ENTRY;
154         CDEBUG(D_INODE, "removing inode %ld page %p, wreq: %p\n",
155                inode->i_ino, page, wreq);
156         rc = obdfs_brw(WRITE, inode, page, 1);
157         /* XXX probably should handle error here somehow.  I think that
158          *     ext2 also does the same thing - discard write even if error?
159          */
160         put_page(page);
161         list_del(&wreq->wb_list);
162         kmem_cache_free(obdfs_wreq_cachep, wreq);
163
164         EXIT;
165         return rc;
166 }
167
168 /*
169  * Add a page to the write request cache list for later writing
170  */
171 static int
172 obdfs_add_to_page_cache(struct inode *inode, struct page *page)
173 {
174         struct obdfs_wreq *wreq;
175
176         ENTRY;
177         wreq = kmem_cache_alloc(obdfs_wreq_cachep, SLAB_KERNEL);
178         CDEBUG(D_INODE, "adding inode %ld page %p, wreq: %p\n",
179                inode->i_ino, page, wreq);
180         if (!wreq) {
181                 EXIT;
182                 return -ENOMEM;
183         }
184         memset(wreq, 0, sizeof(*wreq)); 
185
186         wreq->wb_page = page;
187         wreq->wb_inode = inode;
188
189         CDEBUG(D_INODE, "getting page %p\n", wreq->wb_page);
190         get_page(wreq->wb_page);
191         CDEBUG(D_INODE, "adding wreq %p to inode %p\n", wreq, inode);
192         { struct obdfs_inode_info *oinfo = OBD_INFO(inode);
193                 CDEBUG(D_INODE, "generic is %p\n", inode->u.generic_ip);
194                 CDEBUG(D_INODE, "oinfo is %p\n", oinfo);
195         }
196         CDEBUG(D_INODE, "wreq_list %p\n", &wreq->wb_list);
197         return -EIO;
198         CDEBUG(D_INODE, "inode_list: next %p, prev %p\n", OBD_LIST(inode).next,
199                OBD_LIST(inode).prev);
200         CDEBUG(D_INODE, "inode_list_addr: %p\n", &OBD_LIST(inode));
201
202         list_add(&wreq->wb_list, &OBD_LIST(inode));
203
204         /* For testing purposes, we write out the page here.
205          * In the future, a flush daemon will write out the page.
206          */
207         printk(KERN_INFO "finding page in cache for write\n");
208         wreq = obdfs_find_in_page_cache(inode, page);
209         if (!wreq) {
210                 CDEBUG(D_INODE, "XXXX Can't find page after adding it!!!\n");
211                 EXIT;
212                 return -EINVAL;
213         }
214
215         EXIT;
216         return obdfs_remove_from_page_cache(wreq);
217 }
218
219
220 int obdfs_do_writepage(struct inode *inode, struct page *page, int sync)
221 {
222         int rc;
223
224         ENTRY;
225         PDEBUG(page, "WRITEPAGE");
226         if ( sync ) {
227                 rc = obdfs_brw(WRITE, inode, page, 1);
228         } else {
229                 /* XXX flush stuff */
230                 rc = obdfs_add_to_page_cache(inode, page);
231         }
232                 
233         if (!rc)
234                 SetPageUptodate(page);
235         PDEBUG(page,"WRITEPAGE");
236         return rc;
237 }
238
239 /* returns the page unlocked, but with a reference */
240 int obdfs_writepage(struct dentry *dentry, struct page *page)
241 {
242         return obdfs_do_writepage(dentry->d_inode, page, 0);
243 }
244
245 /*
246  * This does the "real" work of the write. The generic routine has
247  * allocated the page, locked it, done all the page alignment stuff
248  * calculations etc. Now we should just copy the data from user
249  * space and write it back to the real medium..
250  *
251  * If the writer ends up delaying the write, the writer needs to
252  * increment the page use counts until he is done with the page.
253  */
254 int obdfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
255 {
256         long status;
257         struct inode *inode = file->f_dentry->d_inode;
258
259         ENTRY;
260         if ( !Page_Uptodate(page) ) {
261                 status =  obdfs_brw(READ, inode, page, 1);
262                 if (!status) {
263                         SetPageUptodate(page);
264                 } else { 
265                         return status;
266                 }
267         }
268         bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
269         status = -EFAULT;
270
271         if (bytes) {
272                 lock_kernel();
273                 status = obdfs_writepage(file->f_dentry, page);
274                 unlock_kernel();
275         }
276         EXIT;
277         if ( status != PAGE_SIZE ) 
278                 return status;
279         else
280                 return bytes;
281 }
282
283 /* 
284    return an up to date page:
285     - if locked is true then is returned locked
286     - if create is true the corresponding disk blocks are created 
287     - page is held, i.e. caller must release the page
288
289    modeled on NFS code.
290 */
291 struct page *obdfs_getpage(struct inode *inode, unsigned long offset, int create, int locked)
292 {
293         struct page *page_cache;
294         struct page ** hash;
295         struct page * page;
296         int rc;
297
298         ENTRY;
299
300         offset = offset & PAGE_CACHE_MASK;
301         CDEBUG(D_INODE, "\n");
302         
303         page = NULL;
304         page_cache = page_cache_alloc();
305         if ( ! page_cache ) 
306                 return NULL;
307         CDEBUG(D_INODE, "page_cache %p\n", page_cache);
308
309         hash = page_hash(&inode->i_data, offset);
310         page = grab_cache_page(&inode->i_data, offset);
311
312         /* Yuck, no page */
313         if (! page) {
314             printk("grab_cache_page says no dice ...\n");
315             return 0;
316         }
317
318         PDEBUG(page, "GETPAGE: got page - before reading\n");
319         /* now check if the data in the page is up to date */
320         if ( Page_Uptodate(page)) { 
321                 if (!locked)
322                         UnlockPage(page);
323                 EXIT;
324                 return page;
325         } 
326
327         rc = obdfs_brw(READ, inode, page, create);
328
329         if ( rc != PAGE_SIZE ) {
330                 SetPageError(page);
331                 UnlockPage(page);
332                 return page;
333         }
334
335         if ( !locked )
336                 UnlockPage(page);
337         SetPageUptodate(page);
338         PDEBUG(page,"GETPAGE - after reading");
339         EXIT;
340         return page;
341 }
342
343