Whamcloud - gitweb
Pass inline data from filesystem through obdo to VFS.
[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         get_page(wreq->wb_page);
190         list_add(&wreq->wb_list, &OBD_LIST(inode));
191
192         /* For testing purposes, we write out the page here.
193          * In the future, a flush daemon will write out the page.
194          */
195         wreq = obdfs_find_in_page_cache(inode, page);
196         if (!wreq) {
197                 CDEBUG(D_INODE, "XXXX Can't find page after adding it!!!\n");
198                 return -EINVAL;
199         } else
200                 return obdfs_remove_from_page_cache(wreq);
201
202         return 0;
203 }
204
205
206 int obdfs_do_writepage(struct inode *inode, struct page *page, int sync)
207 {
208         int rc;
209
210         ENTRY;
211         PDEBUG(page, "WRITEPAGE");
212         if ( sync ) {
213                 rc = obdfs_brw(WRITE, inode, page, 1);
214         } else {
215                 /* XXX flush stuff */
216                 rc = obdfs_add_to_page_cache(inode, page);
217         }
218                 
219         if (!rc)
220                 SetPageUptodate(page);
221         PDEBUG(page,"WRITEPAGE");
222         return rc;
223 }
224
225 /* returns the page unlocked, but with a reference */
226 int obdfs_writepage(struct dentry *dentry, struct page *page)
227 {
228         return obdfs_do_writepage(dentry->d_inode, page, 0);
229 }
230
231 /*
232  * This does the "real" work of the write. The generic routine has
233  * allocated the page, locked it, done all the page alignment stuff
234  * calculations etc. Now we should just copy the data from user
235  * space and write it back to the real medium..
236  *
237  * If the writer ends up delaying the write, the writer needs to
238  * increment the page use counts until he is done with the page.
239  */
240 int obdfs_write_one_page(struct file *file, struct page *page, unsigned long offset, unsigned long bytes, const char * buf)
241 {
242         long status;
243         struct inode *inode = file->f_dentry->d_inode;
244
245         ENTRY;
246         if ( !Page_Uptodate(page) ) {
247                 status =  obdfs_brw(READ, inode, page, 1);
248                 if (!status) {
249                         SetPageUptodate(page);
250                 } else { 
251                         return status;
252                 }
253         }
254         bytes -= copy_from_user((u8*)page_address(page) + offset, buf, bytes);
255         status = -EFAULT;
256
257         if (bytes) {
258                 lock_kernel();
259                 status = obdfs_writepage(file->f_dentry, page);
260                 unlock_kernel();
261         }
262         EXIT;
263         if ( status != PAGE_SIZE ) 
264                 return status;
265         else
266                 return bytes;
267 }
268
269 /* 
270    return an up to date page:
271     - if locked is true then is returned locked
272     - if create is true the corresponding disk blocks are created 
273     - page is held, i.e. caller must release the page
274
275    modeled on NFS code.
276 */
277 struct page *obdfs_getpage(struct inode *inode, unsigned long offset, int create, int locked)
278 {
279         struct page *page_cache;
280         struct page ** hash;
281         struct page * page;
282         int rc;
283
284         ENTRY;
285
286         offset = offset & PAGE_CACHE_MASK;
287         CDEBUG(D_INODE, "\n");
288         
289         page = NULL;
290         page_cache = page_cache_alloc();
291         if ( ! page_cache ) 
292                 return NULL;
293         CDEBUG(D_INODE, "page_cache %p\n", page_cache);
294
295         hash = page_hash(&inode->i_data, offset);
296         page = grab_cache_page(&inode->i_data, offset);
297
298         /* Yuck, no page */
299         if (! page) {
300             printk("grab_cache_page says no dice ...\n");
301             return 0;
302         }
303
304         PDEBUG(page, "GETPAGE: got page - before reading\n");
305         /* now check if the data in the page is up to date */
306         if ( Page_Uptodate(page)) { 
307                 if (!locked)
308                         UnlockPage(page);
309                 EXIT;
310                 return page;
311         } 
312
313         rc = obdfs_brw(READ, inode, page, create);
314
315         if ( rc != PAGE_SIZE ) {
316                 SetPageError(page);
317                 UnlockPage(page);
318                 return page;
319         }
320
321         if ( !locked )
322                 UnlockPage(page);
323         SetPageUptodate(page);
324         PDEBUG(page,"GETPAGE - after reading");
325         EXIT;
326         return page;
327 }
328
329