Whamcloud - gitweb
Updated for new obdo methods.
[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 /*
103  * Find a specific page in the page cache.  If it is found, we return
104  * the write request struct associated with it, if not found return NULL.
105  */
106 static struct obdfs_wreq *
107 obdfs_find_in_page_cache(struct inode *inode, struct page *page)
108 {
109         struct list_head *list_head = &OBD_LIST(inode);
110         struct obdfs_wreq *head, *wreq;
111
112         ENTRY;
113         CDEBUG(D_INODE, "looking for inode %ld page %p\n", inode->i_ino, page);
114         if (list_empty(list_head)) {
115                 CDEBUG(D_INODE, "empty list\n");
116                 EXIT;
117                 return NULL;
118         }
119         wreq = head = WREQ(list_head->next);
120         do {
121                 CDEBUG(D_INODE, "checking page %p\n", wreq->wb_page);
122                 if (wreq->wb_page == page) {
123                         CDEBUG(D_INODE, "found page %p in list\n", page);
124                         EXIT;
125                         return wreq;
126                 }
127         } while ((wreq = WB_NEXT(wreq)) != head);
128
129         EXIT;
130         return NULL;
131 }
132
133
134 /*
135  * Remove a writeback request from a list
136  */
137 static inline int
138 obdfs_remove_from_page_cache(struct obdfs_wreq *wreq)
139 {
140         struct inode *inode = wreq->wb_inode;
141         struct page *page = wreq->wb_page;
142         int rc;
143
144         ENTRY;
145         CDEBUG(D_INODE, "removing inode %ld page %p, wreq: %p\n",
146                inode->i_ino, page, wreq);
147         rc = obdfs_brw(WRITE, inode, page, 1);
148         /* XXX probably should handle error here somehow.  I think that
149          *     ext2 also does the same thing - discard write even if error?
150          */
151         put_page(page);
152         list_del(&wreq->wb_list);
153         kmem_cache_free(obdfs_wreq_cachep, wreq);
154
155         EXIT;
156         return rc;
157 }
158
159 /*
160  * Add a page to the write request cache list for later writing
161  */
162 static int
163 obdfs_add_to_page_cache(struct inode *inode, struct page *page)
164 {
165         struct obdfs_wreq *wreq;
166
167         ENTRY;
168         wreq = kmem_cache_alloc(obdfs_wreq_cachep, SLAB_KERNEL);
169         CDEBUG(D_INODE, "adding inode %ld page %p, wreq: %p\n",
170                inode->i_ino, page, wreq);
171         if (!wreq) {
172                 EXIT;
173                 return -ENOMEM;
174         }
175         memset(wreq, 0, sizeof(*wreq)); 
176
177         wreq->wb_page = page;
178         wreq->wb_inode = inode;
179
180         get_page(wreq->wb_page);
181         list_add(&wreq->wb_list, &OBD_LIST(inode));
182
183         /* For testing purposes, we write out the page here.
184          * In the future, a flush daemon will write out the page.
185          */
186         wreq = obdfs_find_in_page_cache(inode, page);
187         if (!wreq) {
188                 CDEBUG(D_INODE, "XXXX Can't find page after adding it!!!\n");
189                 return -EINVAL;
190         } else
191                 return obdfs_remove_from_page_cache(wreq);
192
193         return 0;
194 }
195
196
197 int obdfs_do_writepage(struct inode *inode, struct page *page, int sync)
198 {
199         int rc;
200
201         ENTRY;
202         PDEBUG(page, "WRITEPAGE");
203         if ( sync ) {
204                 rc = obdfs_brw(WRITE, inode, page, 1);
205         } else {
206                 /* XXX flush stuff */
207                 rc = obdfs_add_to_page_cache(inode, page);
208         }
209                 
210         if (!rc)
211                 SetPageUptodate(page);
212         PDEBUG(page,"WRITEPAGE");
213         return rc;
214 }
215
216 /* returns the page unlocked, but with a reference */
217 int obdfs_writepage(struct dentry *dentry, struct page *page)
218 {
219         return obdfs_do_writepage(dentry->d_inode, page, 0);
220 }
221
222 /*
223  * This does the "real" work of the write. The generic routine has
224  * allocated the page, locked it, done all the page alignment stuff
225  * calculations etc. Now we should just copy the data from user
226  * space and write it back to the real medium..
227  *
228  * If the writer ends up delaying the write, the writer needs to
229  * increment the page use counts until he is done with the page.
230  */
231 int obdfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
232 {
233         long status;
234         struct inode *inode = file->f_dentry->d_inode;
235
236         ENTRY;
237         if ( !Page_Uptodate(page) ) {
238                 status =  obdfs_brw(READ, inode, page, 1);
239                 if (!status) {
240                         SetPageUptodate(page);
241                 } else { 
242                         return status;
243                 }
244         }
245         bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
246         status = -EFAULT;
247
248         if (bytes) {
249                 lock_kernel();
250                 status = obdfs_writepage(file->f_dentry, page);
251                 unlock_kernel();
252         }
253         EXIT;
254         if ( status != PAGE_SIZE ) 
255                 return status;
256         else
257                 return bytes;
258 }
259
260 /* 
261    return an up to date page:
262     - if locked is true then is returned locked
263     - if create is true the corresponding disk blocks are created 
264     - page is held, i.e. caller must release the page
265
266    modeled on NFS code.
267 */
268 struct page *obdfs_getpage(struct inode *inode, unsigned long offset, int create, int locked)
269 {
270         struct page *page_cache;
271         struct page ** hash;
272         struct page * page;
273         int rc;
274
275         ENTRY;
276
277         offset = offset & PAGE_CACHE_MASK;
278         CDEBUG(D_INODE, "\n");
279         
280         page = NULL;
281         page_cache = page_cache_alloc();
282         if ( ! page_cache ) 
283                 return NULL;
284         CDEBUG(D_INODE, "page_cache %p\n", page_cache);
285
286         hash = page_hash(&inode->i_data, offset);
287         page = grab_cache_page(&inode->i_data, offset);
288
289         /* Yuck, no page */
290         if (! page) {
291             printk("grab_cache_page says no dice ...\n");
292             return 0;
293         }
294
295         PDEBUG(page, "GETPAGE: got page - before reading\n");
296         /* now check if the data in the page is up to date */
297         if ( Page_Uptodate(page)) { 
298                 if (!locked)
299                         UnlockPage(page);
300                 EXIT;
301                 return page;
302         } 
303
304         rc = obdfs_brw(READ, inode, page, create);
305
306         if ( rc != PAGE_SIZE ) {
307                 SetPageError(page);
308                 UnlockPage(page);
309                 return page;
310         }
311
312         if ( !locked )
313                 UnlockPage(page);
314         SetPageUptodate(page);
315         PDEBUG(page,"GETPAGE - after reading");
316         EXIT;
317         return page;
318 }
319
320