Commit | Line | Data |
---|---|---|
b3b94faa DT |
1 | /* |
2 | * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. | |
3a8a9a10 | 3 | * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved. |
b3b94faa DT |
4 | * |
5 | * This copyrighted material is made available to anyone wishing to use, | |
6 | * modify, copy, or redistribute it subject to the terms and conditions | |
7 | * of the GNU General Public License v.2. | |
8 | */ | |
9 | ||
10 | #include <linux/sched.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/spinlock.h> | |
13 | #include <linux/completion.h> | |
14 | #include <linux/buffer_head.h> | |
15 | #include <linux/pagemap.h> | |
16 | #include <linux/mm.h> | |
5c676f6d | 17 | #include <linux/gfs2_ondisk.h> |
b3b94faa DT |
18 | |
19 | #include "gfs2.h" | |
5c676f6d SW |
20 | #include "lm_interface.h" |
21 | #include "incore.h" | |
b3b94faa DT |
22 | #include "bmap.h" |
23 | #include "inode.h" | |
24 | #include "page.h" | |
25 | #include "trans.h" | |
257f9b4e | 26 | #include "ops_address.h" |
5c676f6d | 27 | #include "util.h" |
b3b94faa DT |
28 | |
29 | /** | |
30 | * gfs2_pte_inval - Sync and invalidate all PTEs associated with a glock | |
31 | * @gl: the glock | |
32 | * | |
33 | */ | |
34 | ||
35 | void gfs2_pte_inval(struct gfs2_glock *gl) | |
36 | { | |
37 | struct gfs2_inode *ip; | |
38 | struct inode *inode; | |
39 | ||
5c676f6d | 40 | ip = gl->gl_object; |
feaa7bba | 41 | inode = &ip->i_inode; |
b3b94faa DT |
42 | if (!ip || !S_ISREG(ip->i_di.di_mode)) |
43 | return; | |
44 | ||
45 | if (!test_bit(GIF_PAGED, &ip->i_flags)) | |
46 | return; | |
47 | ||
feaa7bba | 48 | unmap_shared_mapping_range(inode->i_mapping, 0, 0); |
b3b94faa | 49 | |
feaa7bba SW |
50 | if (test_bit(GIF_SW_PAGED, &ip->i_flags)) |
51 | set_bit(GLF_DIRTY, &gl->gl_flags); | |
b3b94faa DT |
52 | |
53 | clear_bit(GIF_SW_PAGED, &ip->i_flags); | |
54 | } | |
55 | ||
56 | /** | |
57 | * gfs2_page_inval - Invalidate all pages associated with a glock | |
58 | * @gl: the glock | |
59 | * | |
60 | */ | |
61 | ||
62 | void gfs2_page_inval(struct gfs2_glock *gl) | |
63 | { | |
64 | struct gfs2_inode *ip; | |
65 | struct inode *inode; | |
66 | ||
5c676f6d | 67 | ip = gl->gl_object; |
feaa7bba | 68 | inode = &ip->i_inode; |
b3b94faa DT |
69 | if (!ip || !S_ISREG(ip->i_di.di_mode)) |
70 | return; | |
71 | ||
feaa7bba SW |
72 | truncate_inode_pages(inode->i_mapping, 0); |
73 | gfs2_assert_withdraw(GFS2_SB(&ip->i_inode), !inode->i_mapping->nrpages); | |
b3b94faa DT |
74 | clear_bit(GIF_PAGED, &ip->i_flags); |
75 | } | |
76 | ||
77 | /** | |
78 | * gfs2_page_sync - Sync the data pages (not metadata) associated with a glock | |
79 | * @gl: the glock | |
80 | * @flags: DIO_START | DIO_WAIT | |
81 | * | |
82 | * Syncs data (not metadata) for a regular file. | |
83 | * No-op for all other types. | |
84 | */ | |
85 | ||
86 | void gfs2_page_sync(struct gfs2_glock *gl, int flags) | |
87 | { | |
88 | struct gfs2_inode *ip; | |
89 | struct inode *inode; | |
feaa7bba SW |
90 | struct address_space *mapping; |
91 | int error = 0; | |
b3b94faa | 92 | |
5c676f6d | 93 | ip = gl->gl_object; |
feaa7bba | 94 | inode = &ip->i_inode; |
b3b94faa DT |
95 | if (!ip || !S_ISREG(ip->i_di.di_mode)) |
96 | return; | |
97 | ||
feaa7bba | 98 | mapping = inode->i_mapping; |
b3b94faa | 99 | |
feaa7bba SW |
100 | if (flags & DIO_START) |
101 | filemap_fdatawrite(mapping); | |
102 | if (!error && (flags & DIO_WAIT)) | |
103 | error = filemap_fdatawait(mapping); | |
b3b94faa | 104 | |
feaa7bba SW |
105 | /* Put back any errors cleared by filemap_fdatawait() |
106 | so they can be caught by someone who can pass them | |
107 | up to user space. */ | |
b3b94faa | 108 | |
feaa7bba SW |
109 | if (error == -ENOSPC) |
110 | set_bit(AS_ENOSPC, &mapping->flags); | |
111 | else if (error) | |
112 | set_bit(AS_EIO, &mapping->flags); | |
b3b94faa | 113 | |
b3b94faa DT |
114 | } |
115 | ||
116 | /** | |
117 | * gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page | |
118 | * @ip: the inode | |
119 | * @dibh: the dinode buffer | |
120 | * @block: the block number that was allocated | |
121 | * @private: any locked page held by the caller process | |
122 | * | |
123 | * Returns: errno | |
124 | */ | |
125 | ||
126 | int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, | |
127 | uint64_t block, void *private) | |
128 | { | |
feaa7bba SW |
129 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); |
130 | struct inode *inode = &ip->i_inode; | |
b3b94faa DT |
131 | struct page *page = (struct page *)private; |
132 | struct buffer_head *bh; | |
133 | int release = 0; | |
134 | ||
135 | if (!page || page->index) { | |
136 | page = grab_cache_page(inode->i_mapping, 0); | |
137 | if (!page) | |
138 | return -ENOMEM; | |
139 | release = 1; | |
140 | } | |
141 | ||
142 | if (!PageUptodate(page)) { | |
143 | void *kaddr = kmap(page); | |
144 | ||
1b50259b | 145 | memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), |
b3b94faa | 146 | ip->i_di.di_size); |
1b50259b | 147 | memset(kaddr + ip->i_di.di_size, 0, |
b3b94faa DT |
148 | PAGE_CACHE_SIZE - ip->i_di.di_size); |
149 | kunmap(page); | |
150 | ||
151 | SetPageUptodate(page); | |
152 | } | |
153 | ||
154 | if (!page_has_buffers(page)) | |
155 | create_empty_buffers(page, 1 << inode->i_blkbits, | |
156 | (1 << BH_Uptodate)); | |
157 | ||
158 | bh = page_buffers(page); | |
159 | ||
160 | if (!buffer_mapped(bh)) | |
161 | map_bh(bh, inode->i_sb, block); | |
162 | ||
163 | set_buffer_uptodate(bh); | |
18ec7d5c SW |
164 | if ((sdp->sd_args.ar_data == GFS2_DATA_ORDERED) || gfs2_is_jdata(ip)) |
165 | gfs2_trans_add_bh(ip->i_gl, bh, 0); | |
b3b94faa DT |
166 | mark_buffer_dirty(bh); |
167 | ||
168 | if (release) { | |
169 | unlock_page(page); | |
170 | page_cache_release(page); | |
171 | } | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | /** | |
257f9b4e | 177 | * gfs2_block_truncate_page - Deal with zeroing out data for truncate |
b3b94faa | 178 | * |
257f9b4e | 179 | * This is partly borrowed from ext3. |
b3b94faa | 180 | */ |
257f9b4e | 181 | int gfs2_block_truncate_page(struct address_space *mapping) |
b3b94faa | 182 | { |
257f9b4e | 183 | struct inode *inode = mapping->host; |
feaa7bba SW |
184 | struct gfs2_inode *ip = GFS2_I(inode); |
185 | struct gfs2_sbd *sdp = GFS2_SB(inode); | |
257f9b4e SW |
186 | loff_t from = inode->i_size; |
187 | unsigned long index = from >> PAGE_CACHE_SHIFT; | |
188 | unsigned offset = from & (PAGE_CACHE_SIZE-1); | |
189 | unsigned blocksize, iblock, length, pos; | |
b3b94faa | 190 | struct buffer_head *bh; |
257f9b4e | 191 | struct page *page; |
b3b94faa | 192 | void *kaddr; |
257f9b4e SW |
193 | int err; |
194 | ||
195 | page = grab_cache_page(mapping, index); | |
196 | if (!page) | |
197 | return 0; | |
b3b94faa | 198 | |
257f9b4e SW |
199 | blocksize = inode->i_sb->s_blocksize; |
200 | length = blocksize - (offset & (blocksize - 1)); | |
201 | iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); | |
b3b94faa DT |
202 | |
203 | if (!page_has_buffers(page)) | |
257f9b4e | 204 | create_empty_buffers(page, blocksize, 0); |
b3b94faa | 205 | |
257f9b4e SW |
206 | /* Find the buffer that contains "offset" */ |
207 | bh = page_buffers(page); | |
208 | pos = blocksize; | |
209 | while (offset >= pos) { | |
210 | bh = bh->b_this_page; | |
211 | iblock++; | |
212 | pos += blocksize; | |
213 | } | |
b3b94faa | 214 | |
257f9b4e | 215 | err = 0; |
b3b94faa | 216 | |
257f9b4e SW |
217 | if (!buffer_mapped(bh)) { |
218 | gfs2_get_block(inode, iblock, bh, 0); | |
219 | /* unmapped? It's a hole - nothing to do */ | |
220 | if (!buffer_mapped(bh)) | |
221 | goto unlock; | |
222 | } | |
223 | ||
224 | /* Ok, it's mapped. Make sure it's up-to-date */ | |
225 | if (PageUptodate(page)) | |
226 | set_buffer_uptodate(bh); | |
227 | ||
228 | if (!buffer_uptodate(bh)) { | |
229 | err = -EIO; | |
230 | ll_rw_block(READ, 1, &bh); | |
231 | wait_on_buffer(bh); | |
232 | /* Uhhuh. Read error. Complain and punt. */ | |
233 | if (!buffer_uptodate(bh)) | |
234 | goto unlock; | |
235 | } | |
236 | ||
18ec7d5c SW |
237 | if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED || gfs2_is_jdata(ip)) |
238 | gfs2_trans_add_bh(ip->i_gl, bh, 0); | |
b3b94faa | 239 | |
257f9b4e SW |
240 | kaddr = kmap_atomic(page, KM_USER0); |
241 | memset(kaddr + offset, 0, length); | |
242 | flush_dcache_page(page); | |
243 | kunmap_atomic(kaddr, KM_USER0); | |
244 | ||
245 | unlock: | |
b3b94faa DT |
246 | unlock_page(page); |
247 | page_cache_release(page); | |
257f9b4e | 248 | return err; |
b3b94faa DT |
249 | } |
250 | ||
257f9b4e | 251 | void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, |
b3b94faa DT |
252 | unsigned int from, unsigned int to) |
253 | { | |
254 | struct buffer_head *head = page_buffers(page); | |
255 | unsigned int bsize = head->b_size; | |
256 | struct buffer_head *bh; | |
257 | unsigned int start, end; | |
258 | ||
29937ac6 | 259 | for (bh = head, start = 0; bh != head || !start; |
b3b94faa DT |
260 | bh = bh->b_this_page, start = end) { |
261 | end = start + bsize; | |
262 | if (end <= from || start >= to) | |
263 | continue; | |
18ec7d5c | 264 | gfs2_trans_add_bh(ip->i_gl, bh, 0); |
b3b94faa DT |
265 | } |
266 | } | |
267 |