Whamcloud - gitweb
some minor fix in copy_back_pages
[fs/lustre-release.git] / lustre / snapfs / file.c
1 /*
2  * file.c
3  */
4
5 #define DEBUG_SUBSYSTEM S_SNAP
6
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/string.h>
10 #include <linux/slab.h>
11 #include <linux/stat.h>
12 #include <linux/unistd.h>
13 #include <linux/pagemap.h>
14 #include <linux/jbd.h>
15 #include <linux/ext3_fs.h>
16 #include <linux/snap.h>
17
18 #include "snapfs_internal.h" 
19
20 static int has_pages(struct inode *inode, int index)
21 {
22         unsigned long offset = index << PAGE_CACHE_SHIFT;
23         unsigned long blk_start = offset >> inode->i_sb->s_blocksize_bits; 
24         unsigned long blk_end = (offset + PAGE_CACHE_SIZE) >> 
25                                 inode->i_sb->s_blocksize_bits; 
26
27         while (blk_start <= blk_end) {
28                 if (inode->i_mapping && inode->i_mapping->a_ops) {
29                         if (inode->i_mapping->a_ops->bmap(inode->i_mapping, 
30                                                           blk_start))
31                                 return 1;
32                 }
33                 blk_start++;
34         }
35         return 0;
36 }
37
38 static int copy_back_page(struct inode *dst, 
39                           struct inode *src,
40                           unsigned long start,
41                           unsigned long end)
42 {
43         char *kaddr_src, *kaddr_dst;
44         struct snap_cache *cache;
45         struct address_space_operations *c_aops;
46         struct page *src_page, *dst_page;
47         unsigned long index, offset, bytes;
48         int    err = 0;
49         ENTRY;
50
51         index = start >> PAGE_CACHE_SHIFT;
52         bytes = end - start;
53         offset = start & PAGE_CACHE_MASK;
54
55         if (!has_pages(src, index)) 
56                 RETURN(0);
57
58         cache = snap_find_cache(src->i_dev);
59         if (!cache) 
60                 RETURN(-EINVAL);
61         c_aops = filter_c2cfaops(cache->cache_filter);
62         
63         if (!c_aops) 
64                 RETURN(-EINVAL);
65
66         src_page = grab_cache_page(src->i_mapping, index);
67         if (!src_page) {
68                 CERROR("copy block %lu from %lu to %lu ENOMEM \n",
69                           index, src->i_ino, dst->i_ino);
70                 RETURN(-ENOMEM);
71         }
72         
73         c_aops->readpage(NULL, src_page);
74         wait_on_page(src_page);
75         
76         kaddr_src = kmap(src_page);
77         if (!Page_Uptodate(src_page)) {
78                 CERROR("Can not read page index %lu of inode %lu\n",
79                           index, src->i_ino);
80                 err = -EIO;
81                 goto unlock_src_page;
82         }
83         dst_page = grab_cache_page(dst->i_mapping, index);
84         if (!dst_page) {
85                 CERROR("copy block %lu from %lu to %lu ENOMEM \n",
86                           index, src->i_ino, dst->i_ino);
87                 err = -ENOMEM;
88                 goto unlock_src_page;
89         }       
90         kaddr_dst = kmap(dst_page);
91
92         err = c_aops->prepare_write(NULL, dst_page, offset, bytes);
93         if (err) 
94                 goto unlock_dst_page; 
95         memcpy(kaddr_dst, kaddr_src, PAGE_CACHE_SIZE);
96         flush_dcache_page(dst_page);
97
98         err = c_aops->commit_write(NULL, dst_page, offset, bytes);
99         if (err) 
100                 goto unlock_dst_page; 
101         err = 1;
102 unlock_dst_page:
103         kunmap(dst_page);
104         UnlockPage(dst_page);
105         page_cache_release(dst_page);
106 unlock_src_page:
107         kunmap(src_page);
108         page_cache_release(src_page);
109         RETURN(err);
110 }
111
112 static ssize_t currentfs_write (struct file *filp, const char *buf, 
113                                 size_t count, loff_t *ppos)
114 {
115         struct snap_cache *cache;
116         struct inode *inode = filp->f_dentry->d_inode;
117         struct file_operations *fops;
118         long   start[2]={-1,-1}, end[2]={-1,-1};
119         struct snap_table *table;
120         struct inode *cache_inode = NULL;
121         int slot = 0, index = 0, result = 0;
122         long i;
123         ssize_t rc;
124         loff_t pos;
125   
126         ENTRY;
127
128         if (currentfs_is_under_dotsnap(filp->f_dentry)) 
129                 RETURN(-ENOSPC);
130
131         cache = snap_find_cache(inode->i_dev);
132         if ( !cache ) 
133                 RETURN(-EINVAL);
134
135         if ( snap_needs_cow(inode) != -1 ) {
136                 CDEBUG(D_SNAP, "snap_needs_cow for ino %lu \n",inode->i_ino);
137                 snap_do_cow(inode, filp->f_dentry->d_parent->d_inode->i_ino, 0);
138         }
139
140         fops = filter_c2cffops(cache->cache_filter); 
141         if (!fops || !fops->write) 
142                 RETURN(-EINVAL);
143
144         if (filp->f_flags & O_APPEND)
145                 pos = inode->i_size;
146         else {
147                 pos = *ppos;
148                 if (pos != *ppos)
149                         RETURN(-EINVAL);
150         }
151         if (pos & PAGE_CACHE_MASK) {
152                 start[0] = pos & PAGE_CACHE_MASK;
153                 end[0] = pos;
154         }
155         pos += count - 1;
156         if ((pos+1) & PAGE_CACHE_MASK) {
157                 start[1] = pos;  
158                 end[1] = PAGE_CACHE_ALIGN(pos);
159         }
160
161         if (((start[0] >> PAGE_CACHE_SHIFT) == (start[1] >> PAGE_CACHE_SHIFT)) ||
162             pos > inode->i_size) 
163                 start[1] = -1;
164         
165         for (i = 0; i < 2; i++) {
166                 if (start[i] == -1) 
167                         continue;
168                 table = &snap_tables[cache->cache_snap_tableno];
169                 /*Find the nearest page in snaptable and copy back it*/
170                 for (slot = table->tbl_count - 1; slot >= 1; slot--) {
171                         cache_inode = NULL;
172                         index = table->snap_items[slot].index;
173                         cache_inode = snap_get_indirect(inode, NULL, index);
174
175                         if (!cache_inode)  continue;
176
177                         CDEBUG(D_SNAP, "find cache_ino %lu\n", cache_inode->i_ino);
178                 
179                         result = copy_back_page(inode, cache_inode, start[i], end[i]);
180                         if (result == 1) {
181                                 CDEBUG(D_SNAP, "copy page%lu back from ind %lu to %lu\n", 
182                                        (start[i] >> PAGE_CACHE_SHIFT), 
183                                        cache_inode->i_ino, 
184                                        inode->i_ino);
185                                 iput(cache_inode);
186                                 result = 0;
187                                 break;
188                         }
189                         if (result < 0) {
190                                 iput(cache_inode);
191                                 rc = result;
192                                 goto exit;
193                         }
194                         iput(cache_inode);
195                 }
196         }
197         rc = fops->write(filp, buf, count, ppos);
198 exit:
199         RETURN(rc);
200 }
201
202 static int currentfs_readpage(struct file *file, struct page *page)
203 {
204         struct inode *inode = file->f_dentry->d_inode;
205         unsigned long ind_ino = inode->i_ino;
206         struct inode *pri_inode = NULL;
207         struct inode *cache_inode = NULL;
208         struct address_space_operations *c_aops;
209         struct snap_cache *cache;
210         struct snap_table *table;
211         struct page *cache_page = NULL;
212         int rc = 0, slot = 0, index = 0, search_older = 0;
213         long block;
214
215         ENTRY;
216
217         cache = snap_find_cache(inode->i_dev);
218         if ( !cache ) { 
219                 RETURN(-EINVAL);
220         }
221         
222         c_aops = filter_c2cfaops(cache->cache_filter);
223
224         block = page->index >> inode->i_sb->s_blocksize_bits;
225
226         /* if there is a block in the cache, return the cache readpage */
227         if(c_aops->bmap(inode->i_mapping, block) ) {
228                 CDEBUG(D_SNAP, "block %lu in cache, ino %lu\n", 
229                                 block, inode->i_ino);
230                 rc = c_aops->readpage(file, page);
231                 RETURN(rc);
232         }
233
234         /*
235          * clonefs_readpage will fill this with primary ino number
236          * we need it to follow the cloned chain of primary inode
237          */
238         if( file->f_dentry->d_fsdata ){
239                 pri_inode = iget(inode->i_sb, (unsigned long)file->f_dentry->d_fsdata);
240                 if( !pri_inode )
241                         RETURN(-EINVAL);
242                 inode = pri_inode;
243                 search_older = 1;
244         }
245
246         table = &snap_tables[cache->cache_snap_tableno];
247
248         for (slot = table->tbl_count - 1; slot >= 1; slot--) {
249                 cache_inode = NULL;
250                 index = table->snap_items[slot].index;
251                 cache_inode = snap_get_indirect(inode, NULL, index);
252
253                 if (!cache_inode )  continue;
254
255                 /* we only want slots between cache_inode to the oldest one */
256                 if(search_older && cache_inode->i_ino == ind_ino )
257                         search_older = 0;
258
259                 if (!search_older && c_aops->bmap(cache_inode->i_mapping, block)) 
260                         break;
261                 iput(cache_inode);
262         }
263         if (pri_inode) iput(pri_inode);
264
265         if (!cache_inode )  
266                 RETURN(-EINVAL);
267
268         down(&cache_inode->i_sem);
269
270         /*Here we have changed a file to read,
271          *So we should rewrite generic file read here 
272          *FIXME later, the code is ugly
273          */
274         
275         cache_page = grab_cache_page(cache_inode->i_mapping, page->index);
276         if (!cache_page) 
277                 GOTO(exit_release, rc = -ENOMEM);
278         if ((rc = c_aops->readpage(file, cache_page)))
279                 GOTO(exit_release, 0);
280         
281         wait_on_page(cache_page);
282
283         if (!Page_Uptodate(cache_page))
284                 GOTO(exit_release, rc = -EIO);
285
286         memcpy(kmap(page), kmap(cache_page), PAGE_CACHE_SIZE);
287
288         kunmap(cache_page);
289         page_cache_release(cache_page);
290
291         up(&cache_inode->i_sem);
292         iput(cache_inode);
293         
294         kunmap(page);
295         SetPageUptodate(page);
296         UnlockPage(page);
297
298         RETURN(rc);
299
300 exit_release:
301         if (cache_page) 
302                 page_cache_release(cache_page);
303         up(&cache_inode->i_sem);
304         iput(cache_inode);
305         UnlockPage(page);
306         RETURN(rc);
307 }
308
309 struct address_space_operations currentfs_file_aops = {
310         readpage:       currentfs_readpage,
311 };
312                                                                                                                                                                                                      
313 struct file_operations currentfs_file_fops = {
314         write:          currentfs_write,
315 };
316                                                                                                                                                                                                      
317 struct inode_operations currentfs_file_iops = {
318         revalidate:     NULL,
319 };
320