aboutsummaryrefslogtreecommitdiffstats
path: root/fs/buffer.c
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2007-10-16 04:25:07 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:42:55 -0400
commit89e107877b65bf6eff1d63a1302dee9a091586f5 (patch)
tree8a120a04c46f19229d1cf9b9c546f1818cf84c44 /fs/buffer.c
parent7765ec26ae1c01bb29bedf910e4efcced8cc81d2 (diff)
fs: new cont helpers
Rework the generic block "cont" routines to handle the new aops. Supporting cont_prepare_write would take quite a lot of code to support, so remove it instead (and we later convert all filesystems to use it). write_begin gets passed AOP_FLAG_CONT_EXPAND when called from generic_cont_expand, so filesystems can avoid the old hacks they used. Signed-off-by: Nick Piggin <npiggin@suse.de> Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r--fs/buffer.c194
1 files changed, 94 insertions, 100 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 68b8fbdc1b28..1f1577490417 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2156,14 +2156,14 @@ int block_read_full_page(struct page *page, get_block_t *get_block)
2156} 2156}
2157 2157
2158/* utility function for filesystems that need to do work on expanding 2158/* utility function for filesystems that need to do work on expanding
2159 * truncates. Uses prepare/commit_write to allow the filesystem to 2159 * truncates. Uses filesystem pagecache writes to allow the filesystem to
2160 * deal with the hole. 2160 * deal with the hole.
2161 */ 2161 */
2162static int __generic_cont_expand(struct inode *inode, loff_t size, 2162int generic_cont_expand_simple(struct inode *inode, loff_t size)
2163 pgoff_t index, unsigned int offset)
2164{ 2163{
2165 struct address_space *mapping = inode->i_mapping; 2164 struct address_space *mapping = inode->i_mapping;
2166 struct page *page; 2165 struct page *page;
2166 void *fsdata;
2167 unsigned long limit; 2167 unsigned long limit;
2168 int err; 2168 int err;
2169 2169
@@ -2176,140 +2176,134 @@ static int __generic_cont_expand(struct inode *inode, loff_t size,
2176 if (size > inode->i_sb->s_maxbytes) 2176 if (size > inode->i_sb->s_maxbytes)
2177 goto out; 2177 goto out;
2178 2178
2179 err = -ENOMEM; 2179 err = pagecache_write_begin(NULL, mapping, size, 0,
2180 page = grab_cache_page(mapping, index); 2180 AOP_FLAG_UNINTERRUPTIBLE|AOP_FLAG_CONT_EXPAND,
2181 if (!page) 2181 &page, &fsdata);
2182 goto out; 2182 if (err)
2183 err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
2184 if (err) {
2185 /*
2186 * ->prepare_write() may have instantiated a few blocks
2187 * outside i_size. Trim these off again.
2188 */
2189 unlock_page(page);
2190 page_cache_release(page);
2191 vmtruncate(inode, inode->i_size);
2192 goto out; 2183 goto out;
2193 }
2194 2184
2195 err = mapping->a_ops->commit_write(NULL, page, offset, offset); 2185 err = pagecache_write_end(NULL, mapping, size, 0, 0, page, fsdata);
2186 BUG_ON(err > 0);
2196 2187
2197 unlock_page(page);
2198 page_cache_release(page);
2199 if (err > 0)
2200 err = 0;
2201out: 2188out:
2202 return err; 2189 return err;
2203} 2190}
2204 2191
2205int generic_cont_expand(struct inode *inode, loff_t size) 2192int generic_cont_expand(struct inode *inode, loff_t size)
2206{ 2193{
2207 pgoff_t index;
2208 unsigned int offset; 2194 unsigned int offset;
2209 2195
2210 offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */ 2196 offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
2211 2197
2212 /* ugh. in prepare/commit_write, if from==to==start of block, we 2198 /* ugh. in prepare/commit_write, if from==to==start of block, we
2213 ** skip the prepare. make sure we never send an offset for the start 2199 * skip the prepare. make sure we never send an offset for the start
2214 ** of a block 2200 * of a block.
2215 */ 2201 * XXX: actually, this should be handled in those filesystems by
2202 * checking for the AOP_FLAG_CONT_EXPAND flag.
2203 */
2216 if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { 2204 if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
2217 /* caller must handle this extra byte. */ 2205 /* caller must handle this extra byte. */
2218 offset++; 2206 size++;
2219 } 2207 }
2220 index = size >> PAGE_CACHE_SHIFT; 2208 return generic_cont_expand_simple(inode, size);
2221
2222 return __generic_cont_expand(inode, size, index, offset);
2223}
2224
2225int generic_cont_expand_simple(struct inode *inode, loff_t size)
2226{
2227 loff_t pos = size - 1;
2228 pgoff_t index = pos >> PAGE_CACHE_SHIFT;
2229 unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
2230
2231 /* prepare/commit_write can handle even if from==to==start of block. */
2232 return __generic_cont_expand(inode, size, index, offset);
2233} 2209}
2234 2210
2235/* 2211int cont_expand_zero(struct file *file, struct address_space *mapping,
2236 * For moronic filesystems that do not allow holes in file. 2212 loff_t pos, loff_t *bytes)
2237 * We may have to extend the file.
2238 */
2239
2240int cont_prepare_write(struct page *page, unsigned offset,
2241 unsigned to, get_block_t *get_block, loff_t *bytes)
2242{ 2213{
2243 struct address_space *mapping = page->mapping;
2244 struct inode *inode = mapping->host; 2214 struct inode *inode = mapping->host;
2245 struct page *new_page;
2246 pgoff_t pgpos;
2247 long status;
2248 unsigned zerofrom;
2249 unsigned blocksize = 1 << inode->i_blkbits; 2215 unsigned blocksize = 1 << inode->i_blkbits;
2216 struct page *page;
2217 void *fsdata;
2218 pgoff_t index, curidx;
2219 loff_t curpos;
2220 unsigned zerofrom, offset, len;
2221 int err = 0;
2250 2222
2251 while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) { 2223 index = pos >> PAGE_CACHE_SHIFT;
2252 status = -ENOMEM; 2224 offset = pos & ~PAGE_CACHE_MASK;
2253 new_page = grab_cache_page(mapping, pgpos); 2225
2254 if (!new_page) 2226 while (index > (curidx = (curpos = *bytes)>>PAGE_CACHE_SHIFT)) {
2255 goto out; 2227 zerofrom = curpos & ~PAGE_CACHE_MASK;
2256 /* we might sleep */
2257 if (*bytes>>PAGE_CACHE_SHIFT != pgpos) {
2258 unlock_page(new_page);
2259 page_cache_release(new_page);
2260 continue;
2261 }
2262 zerofrom = *bytes & ~PAGE_CACHE_MASK;
2263 if (zerofrom & (blocksize-1)) { 2228 if (zerofrom & (blocksize-1)) {
2264 *bytes |= (blocksize-1); 2229 *bytes |= (blocksize-1);
2265 (*bytes)++; 2230 (*bytes)++;
2266 } 2231 }
2267 status = __block_prepare_write(inode, new_page, zerofrom, 2232 len = PAGE_CACHE_SIZE - zerofrom;
2268 PAGE_CACHE_SIZE, get_block);
2269 if (status)
2270 goto out_unmap;
2271 zero_user_page(new_page, zerofrom, PAGE_CACHE_SIZE - zerofrom,
2272 KM_USER0);
2273 generic_commit_write(NULL, new_page, zerofrom, PAGE_CACHE_SIZE);
2274 unlock_page(new_page);
2275 page_cache_release(new_page);
2276 }
2277 2233
2278 if (page->index < pgpos) { 2234 err = pagecache_write_begin(file, mapping, curpos, len,
2279 /* completely inside the area */ 2235 AOP_FLAG_UNINTERRUPTIBLE,
2280 zerofrom = offset; 2236 &page, &fsdata);
2281 } else { 2237 if (err)
2282 /* page covers the boundary, find the boundary offset */ 2238 goto out;
2283 zerofrom = *bytes & ~PAGE_CACHE_MASK; 2239 zero_user_page(page, zerofrom, len, KM_USER0);
2240 err = pagecache_write_end(file, mapping, curpos, len, len,
2241 page, fsdata);
2242 if (err < 0)
2243 goto out;
2244 BUG_ON(err != len);
2245 err = 0;
2246 }
2284 2247
2248 /* page covers the boundary, find the boundary offset */
2249 if (index == curidx) {
2250 zerofrom = curpos & ~PAGE_CACHE_MASK;
2285 /* if we will expand the thing last block will be filled */ 2251 /* if we will expand the thing last block will be filled */
2286 if (to > zerofrom && (zerofrom & (blocksize-1))) { 2252 if (offset <= zerofrom) {
2253 goto out;
2254 }
2255 if (zerofrom & (blocksize-1)) {
2287 *bytes |= (blocksize-1); 2256 *bytes |= (blocksize-1);
2288 (*bytes)++; 2257 (*bytes)++;
2289 } 2258 }
2259 len = offset - zerofrom;
2290 2260
2291 /* starting below the boundary? Nothing to zero out */ 2261 err = pagecache_write_begin(file, mapping, curpos, len,
2292 if (offset <= zerofrom) 2262 AOP_FLAG_UNINTERRUPTIBLE,
2293 zerofrom = offset; 2263 &page, &fsdata);
2264 if (err)
2265 goto out;
2266 zero_user_page(page, zerofrom, len, KM_USER0);
2267 err = pagecache_write_end(file, mapping, curpos, len, len,
2268 page, fsdata);
2269 if (err < 0)
2270 goto out;
2271 BUG_ON(err != len);
2272 err = 0;
2294 } 2273 }
2295 status = __block_prepare_write(inode, page, zerofrom, to, get_block); 2274out:
2296 if (status) 2275 return err;
2297 goto out1; 2276}
2298 if (zerofrom < offset) { 2277
2299 zero_user_page(page, zerofrom, offset - zerofrom, KM_USER0); 2278/*
2300 __block_commit_write(inode, page, zerofrom, offset); 2279 * For moronic filesystems that do not allow holes in file.
2280 * We may have to extend the file.
2281 */
2282int cont_write_begin(struct file *file, struct address_space *mapping,
2283 loff_t pos, unsigned len, unsigned flags,
2284 struct page **pagep, void **fsdata,
2285 get_block_t *get_block, loff_t *bytes)
2286{
2287 struct inode *inode = mapping->host;
2288 unsigned blocksize = 1 << inode->i_blkbits;
2289 unsigned zerofrom;
2290 int err;
2291
2292 err = cont_expand_zero(file, mapping, pos, bytes);
2293 if (err)
2294 goto out;
2295
2296 zerofrom = *bytes & ~PAGE_CACHE_MASK;
2297 if (pos+len > *bytes && zerofrom & (blocksize-1)) {
2298 *bytes |= (blocksize-1);
2299 (*bytes)++;
2301 } 2300 }
2302 return 0;
2303out1:
2304 ClearPageUptodate(page);
2305 return status;
2306 2301
2307out_unmap: 2302 *pagep = NULL;
2308 ClearPageUptodate(new_page); 2303 err = block_write_begin(file, mapping, pos, len,
2309 unlock_page(new_page); 2304 flags, pagep, fsdata, get_block);
2310 page_cache_release(new_page);
2311out: 2305out:
2312 return status; 2306 return err;
2313} 2307}
2314 2308
2315int block_prepare_write(struct page *page, unsigned from, unsigned to, 2309int block_prepare_write(struct page *page, unsigned from, unsigned to,
@@ -3191,7 +3185,7 @@ EXPORT_SYMBOL(block_read_full_page);
3191EXPORT_SYMBOL(block_sync_page); 3185EXPORT_SYMBOL(block_sync_page);
3192EXPORT_SYMBOL(block_truncate_page); 3186EXPORT_SYMBOL(block_truncate_page);
3193EXPORT_SYMBOL(block_write_full_page); 3187EXPORT_SYMBOL(block_write_full_page);
3194EXPORT_SYMBOL(cont_prepare_write); 3188EXPORT_SYMBOL(cont_write_begin);
3195EXPORT_SYMBOL(end_buffer_read_sync); 3189EXPORT_SYMBOL(end_buffer_read_sync);
3196EXPORT_SYMBOL(end_buffer_write_sync); 3190EXPORT_SYMBOL(end_buffer_write_sync);
3197EXPORT_SYMBOL(file_fsync); 3191EXPORT_SYMBOL(file_fsync);