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