diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2007-10-15 10:40:33 -0400 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2008-01-25 03:07:13 -0500 |
commit | 3cc3f710ce0effe397b830826a1a081fa81f11c7 (patch) | |
tree | 53f69f1b8d1cbc2849c6bac08ce7786f3ecd7447 /fs/gfs2/ops_file.c | |
parent | 51ff87bdd9f21a5d3672517b75d25ab5842d94a8 (diff) |
[GFS2] Use ->page_mkwrite() for mmap()
This cleans up the mmap() code path for GFS2 by implementing the
page_mkwrite function for GFS2. We are thus able to use the
generic filemap_fault function for our ->fault() implementation.
This now means that shared writable mappings will be much more
efficiently shared across the cluster if there is a reasonable
proportion of read activity (the greater proportion, the better).
As a side effect, it also reduces the size of the code, removes
special cases from readpage and readpages, and makes the code
path easier to follow.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2/ops_file.c')
-rw-r--r-- | fs/gfs2/ops_file.c | 131 |
1 files changed, 121 insertions, 10 deletions
diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index a729c86b8be1..6f3aeb059c61 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c | |||
@@ -33,7 +33,6 @@ | |||
33 | #include "lm.h" | 33 | #include "lm.h" |
34 | #include "log.h" | 34 | #include "log.h" |
35 | #include "meta_io.h" | 35 | #include "meta_io.h" |
36 | #include "ops_vm.h" | ||
37 | #include "quota.h" | 36 | #include "quota.h" |
38 | #include "rgrp.h" | 37 | #include "rgrp.h" |
39 | #include "trans.h" | 38 | #include "trans.h" |
@@ -169,7 +168,7 @@ static int gfs2_get_flags(struct file *filp, u32 __user *ptr) | |||
169 | if (put_user(fsflags, ptr)) | 168 | if (put_user(fsflags, ptr)) |
170 | error = -EFAULT; | 169 | error = -EFAULT; |
171 | 170 | ||
172 | gfs2_glock_dq_m(1, &gh); | 171 | gfs2_glock_dq(&gh); |
173 | gfs2_holder_uninit(&gh); | 172 | gfs2_holder_uninit(&gh); |
174 | return error; | 173 | return error; |
175 | } | 174 | } |
@@ -293,6 +292,125 @@ static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
293 | return -ENOTTY; | 292 | return -ENOTTY; |
294 | } | 293 | } |
295 | 294 | ||
295 | /** | ||
296 | * gfs2_allocate_page_backing - Use bmap to allocate blocks | ||
297 | * @page: The (locked) page to allocate backing for | ||
298 | * | ||
299 | * We try to allocate all the blocks required for the page in | ||
300 | * one go. This might fail for various reasons, so we keep | ||
301 | * trying until all the blocks to back this page are allocated. | ||
302 | * If some of the blocks are already allocated, thats ok too. | ||
303 | */ | ||
304 | |||
305 | static int gfs2_allocate_page_backing(struct page *page) | ||
306 | { | ||
307 | struct inode *inode = page->mapping->host; | ||
308 | struct buffer_head bh; | ||
309 | unsigned long size = PAGE_CACHE_SIZE; | ||
310 | u64 lblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits); | ||
311 | |||
312 | do { | ||
313 | bh.b_state = 0; | ||
314 | bh.b_size = size; | ||
315 | gfs2_block_map(inode, lblock, 1, &bh); | ||
316 | if (!buffer_mapped(&bh)) | ||
317 | return -EIO; | ||
318 | size -= bh.b_size; | ||
319 | lblock += (bh.b_size >> inode->i_blkbits); | ||
320 | } while(size > 0); | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * gfs2_page_mkwrite - Make a shared, mmap()ed, page writable | ||
326 | * @vma: The virtual memory area | ||
327 | * @page: The page which is about to become writable | ||
328 | * | ||
329 | * When the page becomes writable, we need to ensure that we have | ||
330 | * blocks allocated on disk to back that page. | ||
331 | */ | ||
332 | |||
333 | static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page) | ||
334 | { | ||
335 | struct inode *inode = vma->vm_file->f_path.dentry->d_inode; | ||
336 | struct gfs2_inode *ip = GFS2_I(inode); | ||
337 | struct gfs2_sbd *sdp = GFS2_SB(inode); | ||
338 | unsigned long last_index; | ||
339 | u64 pos = page->index << (PAGE_CACHE_SIZE - inode->i_blkbits); | ||
340 | unsigned int data_blocks, ind_blocks, rblocks; | ||
341 | int alloc_required = 0; | ||
342 | struct gfs2_holder gh; | ||
343 | struct gfs2_alloc *al; | ||
344 | int ret; | ||
345 | |||
346 | gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME, &gh); | ||
347 | ret = gfs2_glock_nq_atime(&gh); | ||
348 | if (ret) | ||
349 | goto out; | ||
350 | |||
351 | set_bit(GIF_SW_PAGED, &ip->i_flags); | ||
352 | gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); | ||
353 | ret = gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE, &alloc_required); | ||
354 | if (ret || !alloc_required) | ||
355 | goto out_unlock; | ||
356 | |||
357 | ip->i_alloc.al_requested = 0; | ||
358 | al = gfs2_alloc_get(ip); | ||
359 | ret = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE); | ||
360 | if (ret) | ||
361 | goto out_alloc_put; | ||
362 | ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid); | ||
363 | if (ret) | ||
364 | goto out_quota_unlock; | ||
365 | al->al_requested = data_blocks + ind_blocks; | ||
366 | ret = gfs2_inplace_reserve(ip); | ||
367 | if (ret) | ||
368 | goto out_quota_unlock; | ||
369 | |||
370 | rblocks = RES_DINODE + ind_blocks; | ||
371 | if (gfs2_is_jdata(ip)) | ||
372 | rblocks += data_blocks ? data_blocks : 1; | ||
373 | if (ind_blocks || data_blocks) | ||
374 | rblocks += RES_STATFS + RES_QUOTA; | ||
375 | ret = gfs2_trans_begin(sdp, rblocks, 0); | ||
376 | if (ret) | ||
377 | goto out_trans_fail; | ||
378 | |||
379 | lock_page(page); | ||
380 | ret = -EINVAL; | ||
381 | last_index = ip->i_inode.i_size >> PAGE_CACHE_SHIFT; | ||
382 | if (page->index > last_index) | ||
383 | goto out_unlock_page; | ||
384 | if (!PageUptodate(page) || page->mapping != ip->i_inode.i_mapping) | ||
385 | goto out_unlock_page; | ||
386 | if (gfs2_is_stuffed(ip)) { | ||
387 | ret = gfs2_unstuff_dinode(ip, page); | ||
388 | if (ret) | ||
389 | goto out_unlock_page; | ||
390 | } | ||
391 | ret = gfs2_allocate_page_backing(page); | ||
392 | |||
393 | out_unlock_page: | ||
394 | unlock_page(page); | ||
395 | gfs2_trans_end(sdp); | ||
396 | out_trans_fail: | ||
397 | gfs2_inplace_release(ip); | ||
398 | out_quota_unlock: | ||
399 | gfs2_quota_unlock(ip); | ||
400 | out_alloc_put: | ||
401 | gfs2_alloc_put(ip); | ||
402 | out_unlock: | ||
403 | gfs2_glock_dq(&gh); | ||
404 | out: | ||
405 | gfs2_holder_uninit(&gh); | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | static struct vm_operations_struct gfs2_vm_ops = { | ||
410 | .fault = filemap_fault, | ||
411 | .page_mkwrite = gfs2_page_mkwrite, | ||
412 | }; | ||
413 | |||
296 | 414 | ||
297 | /** | 415 | /** |
298 | * gfs2_mmap - | 416 | * gfs2_mmap - |
@@ -315,14 +433,7 @@ static int gfs2_mmap(struct file *file, struct vm_area_struct *vma) | |||
315 | return error; | 433 | return error; |
316 | } | 434 | } |
317 | 435 | ||
318 | /* This is VM_MAYWRITE instead of VM_WRITE because a call | 436 | vma->vm_ops = &gfs2_vm_ops; |
319 | to mprotect() can turn on VM_WRITE later. */ | ||
320 | |||
321 | if ((vma->vm_flags & (VM_MAYSHARE | VM_MAYWRITE)) == | ||
322 | (VM_MAYSHARE | VM_MAYWRITE)) | ||
323 | vma->vm_ops = &gfs2_vm_ops_sharewrite; | ||
324 | else | ||
325 | vma->vm_ops = &gfs2_vm_ops_private; | ||
326 | 437 | ||
327 | gfs2_glock_dq_uninit(&i_gh); | 438 | gfs2_glock_dq_uninit(&i_gh); |
328 | 439 | ||