diff options
Diffstat (limited to 'fs/gfs2/bmap.c')
-rw-r--r-- | fs/gfs2/bmap.c | 67 |
1 files changed, 62 insertions, 5 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 98fa07c2b710..72b19c5d7807 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -48,6 +48,65 @@ struct strip_mine { | |||
48 | }; | 48 | }; |
49 | 49 | ||
50 | /** | 50 | /** |
51 | * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page | ||
52 | * @ip: the inode | ||
53 | * @dibh: the dinode buffer | ||
54 | * @block: the block number that was allocated | ||
55 | * @private: any locked page held by the caller process | ||
56 | * | ||
57 | * Returns: errno | ||
58 | */ | ||
59 | |||
60 | static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, | ||
61 | uint64_t block, struct page *page) | ||
62 | { | ||
63 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | ||
64 | struct inode *inode = &ip->i_inode; | ||
65 | struct buffer_head *bh; | ||
66 | int release = 0; | ||
67 | |||
68 | if (!page || page->index) { | ||
69 | page = grab_cache_page(inode->i_mapping, 0); | ||
70 | if (!page) | ||
71 | return -ENOMEM; | ||
72 | release = 1; | ||
73 | } | ||
74 | |||
75 | if (!PageUptodate(page)) { | ||
76 | void *kaddr = kmap(page); | ||
77 | |||
78 | memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), | ||
79 | ip->i_di.di_size); | ||
80 | memset(kaddr + ip->i_di.di_size, 0, | ||
81 | PAGE_CACHE_SIZE - ip->i_di.di_size); | ||
82 | kunmap(page); | ||
83 | |||
84 | SetPageUptodate(page); | ||
85 | } | ||
86 | |||
87 | if (!page_has_buffers(page)) | ||
88 | create_empty_buffers(page, 1 << inode->i_blkbits, | ||
89 | (1 << BH_Uptodate)); | ||
90 | |||
91 | bh = page_buffers(page); | ||
92 | |||
93 | if (!buffer_mapped(bh)) | ||
94 | map_bh(bh, inode->i_sb, block); | ||
95 | |||
96 | set_buffer_uptodate(bh); | ||
97 | if ((sdp->sd_args.ar_data == GFS2_DATA_ORDERED) || gfs2_is_jdata(ip)) | ||
98 | gfs2_trans_add_bh(ip->i_gl, bh, 0); | ||
99 | mark_buffer_dirty(bh); | ||
100 | |||
101 | if (release) { | ||
102 | unlock_page(page); | ||
103 | page_cache_release(page); | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /** | ||
51 | * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big | 110 | * gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big |
52 | * @ip: The GFS2 inode to unstuff | 111 | * @ip: The GFS2 inode to unstuff |
53 | * @unstuffer: the routine that handles unstuffing a non-zero length file | 112 | * @unstuffer: the routine that handles unstuffing a non-zero length file |
@@ -59,8 +118,7 @@ struct strip_mine { | |||
59 | * Returns: errno | 118 | * Returns: errno |
60 | */ | 119 | */ |
61 | 120 | ||
62 | int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, | 121 | int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page) |
63 | void *private) | ||
64 | { | 122 | { |
65 | struct buffer_head *bh, *dibh; | 123 | struct buffer_head *bh, *dibh; |
66 | uint64_t block = 0; | 124 | uint64_t block = 0; |
@@ -90,7 +148,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer, | |||
90 | } else { | 148 | } else { |
91 | block = gfs2_alloc_data(ip); | 149 | block = gfs2_alloc_data(ip); |
92 | 150 | ||
93 | error = unstuffer(ip, dibh, block, private); | 151 | error = gfs2_unstuffer_page(ip, dibh, block, page); |
94 | if (error) | 152 | if (error) |
95 | goto out_brelse; | 153 | goto out_brelse; |
96 | } | 154 | } |
@@ -786,8 +844,7 @@ static int do_grow(struct gfs2_inode *ip, uint64_t size) | |||
786 | 844 | ||
787 | if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { | 845 | if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { |
788 | if (gfs2_is_stuffed(ip)) { | 846 | if (gfs2_is_stuffed(ip)) { |
789 | error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, | 847 | error = gfs2_unstuff_dinode(ip, NULL); |
790 | NULL); | ||
791 | if (error) | 848 | if (error) |
792 | goto out_end_trans; | 849 | goto out_end_trans; |
793 | } | 850 | } |