2006-01-16 16:50:04 +00:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 15:09:15 -04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 16:50:04 +00: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 11:05:15 -04:00
* of the GNU General Public License version 2.
2006-01-16 16:50:04 +00:00
*/
# include <linux/sched.h>
# 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-27 17:23:27 -05:00
# include <linux/gfs2_ondisk.h>
2006-09-19 07:56:29 +02:00
# include <linux/lm_interface.h>
2006-01-16 16:50:04 +00:00
# include "gfs2.h"
2006-02-27 17:23:27 -05:00
# include "incore.h"
2006-01-16 16:50:04 +00: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-27 17:23:27 -05:00
# include "util.h"
2006-01-16 16:50:04 +00:00
static void pfault_be_greedy ( struct gfs2_inode * ip )
{
unsigned int time ;
spin_lock ( & ip - > i_spin ) ;
time = ip - > i_greedy ;
ip - > i_last_pfault = jiffies ;
spin_unlock ( & ip - > i_spin ) ;
2006-06-14 15:32:57 -04:00
igrab ( & ip - > i_inode ) ;
2006-01-16 16:50:04 +00:00
if ( gfs2_glock_be_greedy ( ip - > i_gl , time ) )
2006-06-14 15:32:57 -04:00
iput ( & ip - > i_inode ) ;
2006-01-16 16:50:04 +00:00
}
static struct page * gfs2_private_nopage ( struct vm_area_struct * area ,
unsigned long address , int * type )
{
2006-06-14 15:32:57 -04:00
struct gfs2_inode * ip = GFS2_I ( area - > vm_file - > f_mapping - > host ) ;
2006-01-16 16:50:04 +00:00
struct page * result ;
set_bit ( GIF_PAGED , & ip - > i_flags ) ;
result = filemap_nopage ( area , address , type ) ;
if ( result & & result ! = NOPAGE_OOM )
pfault_be_greedy ( ip ) ;
return result ;
}
static int alloc_page_backing ( struct gfs2_inode * ip , struct page * page )
{
2006-06-14 15:32:57 -04:00
struct gfs2_sbd * sdp = GFS2_SB ( & ip - > i_inode ) ;
2006-01-16 16:50:04 +00:00
unsigned long index = page - > index ;
2006-09-04 12:49:07 -04:00
u64 lblock = index < < ( PAGE_CACHE_SHIFT -
2006-02-27 12:00:42 -05:00
sdp - > sd_sb . sb_bsize_shift ) ;
2006-01-16 16:50:04 +00: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 13:23:29 -05:00
error = gfs2_quota_check ( ip , ip - > i_inode . i_uid , ip - > i_inode . i_gid ) ;
2006-01-16 16:50:04 +00:00
if ( error )
goto out_gunlock_q ;
2006-05-05 16:59:11 -04:00
gfs2_write_calc_reserv ( ip , PAGE_CACHE_SIZE , & data_blocks , & ind_blocks ) ;
2006-01-16 16:50:04 +00:00
al - > al_requested = data_blocks + ind_blocks ;
error = gfs2_inplace_reserve ( ip ) ;
if ( error )
goto out_gunlock_q ;
2006-05-05 16:59:11 -04:00
error = gfs2_trans_begin ( sdp , al - > al_rgd - > rd_ri . ri_length +
2006-01-16 16:50:04 +00:00
ind_blocks + RES_DINODE +
RES_STATFS + RES_QUOTA , 0 ) ;
if ( error )
goto out_ipres ;
if ( gfs2_is_stuffed ( ip ) ) {
2006-07-26 10:51:20 -04:00
error = gfs2_unstuff_dinode ( ip , NULL ) ;
2006-01-16 16:50:04 +00:00
if ( error )
goto out_trans ;
}
for ( x = 0 ; x < blocks ; ) {
2006-09-04 12:49:07 -04:00
u64 dblock ;
2006-01-16 16:50:04 +00:00
unsigned int extlen ;
int new = 1 ;
2006-06-14 15:32:57 -04:00
error = gfs2_extent_map ( & ip - > i_inode , lblock , & new , & dblock , & extlen ) ;
2006-01-16 16:50:04 +00:00
if ( error )
goto out_trans ;
lblock + = extlen ;
x + = extlen ;
}
gfs2_assert_warn ( sdp , al - > al_alloced ) ;
2006-09-04 12:04:26 -04:00
out_trans :
2006-01-16 16:50:04 +00:00
gfs2_trans_end ( sdp ) ;
2006-09-04 12:04:26 -04:00
out_ipres :
2006-01-16 16:50:04 +00:00
gfs2_inplace_release ( ip ) ;
2006-09-04 12:04:26 -04:00
out_gunlock_q :
2006-01-16 16:50:04 +00:00
gfs2_quota_unlock ( ip ) ;
2006-09-04 12:04:26 -04:00
out :
2006-01-16 16:50:04 +00:00
gfs2_alloc_put ( ip ) ;
return error ;
}
static struct page * gfs2_sharewrite_nopage ( struct vm_area_struct * area ,
unsigned long address , int * type )
{
2006-08-04 15:41:22 -04:00
struct file * file = area - > vm_file ;
struct gfs2_file * gf = file - > private_data ;
struct gfs2_inode * ip = GFS2_I ( file - > f_mapping - > host ) ;
2006-01-16 16:50:04 +00:00
struct gfs2_holder i_gh ;
struct page * result = NULL ;
2006-02-27 12:00:42 -05:00
unsigned long index = ( ( address - area - > vm_start ) > > PAGE_CACHE_SHIFT ) +
area - > vm_pgoff ;
2006-01-16 16:50:04 +00:00
int alloc_required ;
int error ;
error = gfs2_glock_nq_init ( ip - > i_gl , LM_ST_EXCLUSIVE , 0 , & i_gh ) ;
if ( error )
return NULL ;
set_bit ( GIF_PAGED , & ip - > i_flags ) ;
set_bit ( GIF_SW_PAGED , & ip - > i_flags ) ;
2006-08-04 15:41:22 -04:00
error = gfs2_write_alloc_required ( ip , ( u64 ) index < < PAGE_CACHE_SHIFT ,
2006-01-16 16:50:04 +00:00
PAGE_CACHE_SIZE , & alloc_required ) ;
if ( error )
goto out ;
2006-08-04 15:41:22 -04:00
set_bit ( GFF_EXLOCK , & gf - > f_flags ) ;
2006-01-16 16:50:04 +00:00
result = filemap_nopage ( area , address , type ) ;
2006-08-04 15:41:22 -04:00
clear_bit ( GFF_EXLOCK , & gf - > f_flags ) ;
2006-01-16 16:50:04 +00:00
if ( ! result | | result = = NOPAGE_OOM )
goto out ;
if ( alloc_required ) {
error = alloc_page_backing ( ip , result ) ;
if ( error ) {
page_cache_release ( result ) ;
result = NULL ;
goto out ;
}
set_page_dirty ( result ) ;
}
pfault_be_greedy ( ip ) ;
2006-08-04 15:41:22 -04:00
out :
2006-01-16 16:50:04 +00:00
gfs2_glock_dq_uninit ( & i_gh ) ;
return result ;
}
struct vm_operations_struct gfs2_vm_ops_private = {
. nopage = gfs2_private_nopage ,
} ;
struct vm_operations_struct gfs2_vm_ops_sharewrite = {
. nopage = gfs2_sharewrite_nopage ,
} ;