2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
# include <linux/mm.h>
# include <linux/pagemap.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-09-19 09:56:29 +04:00
# include <linux/lm_interface.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "bmap.h"
# include "glock.h"
# include "inode.h"
# include "ops_vm.h"
# include "quota.h"
# include "rgrp.h"
# include "trans.h"
2006-02-28 01:23:27 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2007-07-19 12:47:03 +04:00
static int gfs2_private_fault ( struct vm_area_struct * vma , struct vm_fault * vmf )
2006-01-16 19:50:04 +03:00
{
2007-07-19 12:46:59 +04:00
struct gfs2_inode * ip = GFS2_I ( vma - > vm_file - > f_mapping - > host ) ;
2006-01-16 19:50:04 +03:00
set_bit ( GIF_PAGED , & ip - > i_flags ) ;
2007-07-19 12:47:03 +04:00
return filemap_fault ( vma , vmf ) ;
2006-01-16 19:50:04 +03:00
}
static int alloc_page_backing ( struct gfs2_inode * ip , struct page * page )
{
2006-06-14 23:32:57 +04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 19:50:04 +03:00
unsigned long index = page - > index ;
2006-09-04 20:49:07 +04:00
u64 lblock = index < < ( PAGE_CACHE_SHIFT -
2006-02-27 20:00:42 +03:00
sdp - > sd_sb . sb_bsize_shift ) ;
2006-01-16 19:50:04 +03:00
unsigned int blocks = PAGE_CACHE_SIZE > > sdp - > sd_sb . sb_bsize_shift ;
struct gfs2_alloc * al ;
unsigned int data_blocks , ind_blocks ;
unsigned int x ;
int error ;
al = gfs2_alloc_get ( ip ) ;
error = gfs2_quota_lock ( ip , NO_QUOTA_CHANGE , NO_QUOTA_CHANGE ) ;
if ( error )
goto out ;
2006-11-01 21:23:29 +03:00
error = gfs2_quota_check ( ip , ip - > i_inode . i_uid , ip - > i_inode . i_gid ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_gunlock_q ;
2006-05-06 00:59:11 +04:00
gfs2_write_calc_reserv ( ip , PAGE_CACHE_SIZE , & data_blocks , & ind_blocks ) ;
2006-01-16 19:50:04 +03:00
al - > al_requested = data_blocks + ind_blocks ;
error = gfs2_inplace_reserve ( ip ) ;
if ( error )
goto out_gunlock_q ;
2007-06-01 17:11:58 +04:00
error = gfs2_trans_begin ( sdp , al - > al_rgd - > rd_length +
2006-01-16 19:50:04 +03:00
ind_blocks + RES_DINODE +
RES_STATFS + RES_QUOTA , 0 ) ;
if ( error )
goto out_ipres ;
if ( gfs2_is_stuffed ( ip ) ) {
2006-07-26 18:51:20 +04:00
error = gfs2_unstuff_dinode ( ip , NULL ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_trans ;
}
for ( x = 0 ; x < blocks ; ) {
2006-09-04 20:49:07 +04:00
u64 dblock ;
2006-01-16 19:50:04 +03:00
unsigned int extlen ;
int new = 1 ;
2006-06-14 23:32:57 +04:00
error = gfs2_extent_map ( & ip - > i_inode , lblock , & new , & dblock , & extlen ) ;
2006-01-16 19:50:04 +03:00
if ( error )
goto out_trans ;
lblock + = extlen ;
x + = extlen ;
}
gfs2_assert_warn ( sdp , al - > al_alloced ) ;
2006-09-04 20:04:26 +04:00
out_trans :
2006-01-16 19:50:04 +03:00
gfs2_trans_end ( sdp ) ;
2006-09-04 20:04:26 +04:00
out_ipres :
2006-01-16 19:50:04 +03:00
gfs2_inplace_release ( ip ) ;
2006-09-04 20:04:26 +04:00
out_gunlock_q :
2006-01-16 19:50:04 +03:00
gfs2_quota_unlock ( ip ) ;
2006-09-04 20:04:26 +04:00
out :
2006-01-16 19:50:04 +03:00
gfs2_alloc_put ( ip ) ;
return error ;
}
2007-07-19 12:47:03 +04:00
static int gfs2_sharewrite_fault ( struct vm_area_struct * vma ,
struct vm_fault * vmf )
2006-01-16 19:50:04 +03:00
{
2007-07-19 12:46:59 +04:00
struct file * file = vma - > vm_file ;
2006-08-04 23:41:22 +04:00
struct gfs2_file * gf = file - > private_data ;
struct gfs2_inode * ip = GFS2_I ( file - > f_mapping - > host ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder i_gh ;
int alloc_required ;
int error ;
2007-07-19 12:47:05 +04:00
int ret = 0 ;
2006-01-16 19:50:04 +03:00
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , & i_gh ) ;
if ( error )
2007-07-19 12:47:03 +04:00
goto out ;
2006-01-16 19:50:04 +03:00
set_bit ( GIF_PAGED , & ip - > i_flags ) ;
set_bit ( GIF_SW_PAGED , & ip - > i_flags ) ;
2007-07-19 12:46:59 +04:00
error = gfs2_write_alloc_required ( ip ,
2007-07-19 12:47:03 +04:00
( u64 ) vmf - > pgoff < < PAGE_CACHE_SHIFT ,
2007-07-19 12:46:59 +04:00
PAGE_CACHE_SIZE , & alloc_required ) ;
if ( error ) {
2007-07-19 12:47:03 +04:00
ret = VM_FAULT_OOM ; /* XXX: are these right? */
goto out_unlock ;
2007-07-19 12:46:59 +04:00
}
2006-01-16 19:50:04 +03:00
2006-08-04 23:41:22 +04:00
set_bit ( GFF_EXLOCK , & gf - > f_flags ) ;
2007-07-19 12:47:03 +04:00
ret = filemap_fault ( vma , vmf ) ;
2006-08-04 23:41:22 +04:00
clear_bit ( GFF_EXLOCK , & gf - > f_flags ) ;
2007-07-19 12:47:05 +04:00
if ( ret & VM_FAULT_ERROR )
2007-07-19 12:47:03 +04:00
goto out_unlock ;
2006-01-16 19:50:04 +03:00
if ( alloc_required ) {
2007-07-19 12:47:03 +04:00
/* XXX: do we need to drop page lock around alloc_page_backing?*/
error = alloc_page_backing ( ip , vmf - > page ) ;
2006-01-16 19:50:04 +03:00
if ( error ) {
2007-07-19 12:47:05 +04:00
/*
* VM_FAULT_LOCKED should always be the case for
* filemap_fault , but it may not be in a future
* implementation .
*/
if ( ret & VM_FAULT_LOCKED )
2007-07-19 12:47:03 +04:00
unlock_page ( vmf - > page ) ;
page_cache_release ( vmf - > page ) ;
ret = VM_FAULT_OOM ;
goto out_unlock ;
2006-01-16 19:50:04 +03:00
}
2007-07-19 12:47:03 +04:00
set_page_dirty ( vmf - > page ) ;
2006-01-16 19:50:04 +03:00
}
2007-07-19 12:47:03 +04:00
out_unlock :
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & i_gh ) ;
2007-07-19 12:47:03 +04:00
out :
return ret ;
2006-01-16 19:50:04 +03:00
}
struct vm_operations_struct gfs2_vm_ops_private = {
2007-07-19 12:46:59 +04:00
. fault = gfs2_private_fault ,
2006-01-16 19:50:04 +03:00
} ;
struct vm_operations_struct gfs2_vm_ops_sharewrite = {
2007-07-19 12:46:59 +04:00
. fault = gfs2_sharewrite_fault ,
2006-01-16 19:50:04 +03:00
} ;