@ -375,21 +375,14 @@ out_err:
/*
* Insert a write request into an inode
*/
static int nfs_inode_add_request ( struct inode * inode , struct nfs_page * req )
static void nfs_inode_add_request ( struct inode * inode , struct nfs_page * req )
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
int error ;
error = radix_tree_preload ( GFP_NOFS ) ;
if ( error ! = 0 )
goto out ;
/* Lock the request! */
nfs_lock_request_dontget ( req ) ;
spin_lock ( & inode - > i_lock ) ;
error = radix_tree_insert ( & nfsi - > nfs_page_tree , req - > wb_index , req ) ;
BUG_ON ( error ) ;
if ( ! nfsi - > npages & & nfs_have_delegation ( inode , FMODE_WRITE ) )
inode - > i_version + + ;
set_bit ( PG_MAPPED , & req - > wb_flags ) ;
@ -398,11 +391,10 @@ static int nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
nfsi - > npages + + ;
kref_get ( & req - > wb_kref ) ;
spin_unlock ( & inode - > i_lock ) ;
radix_tree_preload_end ( ) ;
out :
return error ;
}
static struct pnfs_layout_segment * nfs_clear_request_commit ( struct nfs_page * req ) ;
/*
* Remove a write request from an inode
*/
@ -410,16 +402,18 @@ static void nfs_inode_remove_request(struct nfs_page *req)
{
struct inode * inode = req - > wb_context - > dentry - > d_inode ;
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct pnfs_layout_segment * lseg ;
BUG_ON ( ! NFS_WBACK_BUSY ( req ) ) ;
spin_lock ( & inode - > i_lock ) ;
lseg = nfs_clear_request_commit ( req ) ;
set_page_private ( req - > wb_page , 0 ) ;
ClearPagePrivate ( req - > wb_page ) ;
clear_bit ( PG_MAPPED , & req - > wb_flags ) ;
radix_tree_delete ( & nfsi - > nfs_page_tree , req - > wb_index ) ;
nfsi - > npages - - ;
spin_unlock ( & inode - > i_lock ) ;
put_lseg ( lseg ) ;
nfs_release_request ( req ) ;
}
@ -438,31 +432,38 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
{
struct inode * inode = req - > wb_context - > dentry - > d_inode ;
struct nfs_inode * nfsi = NFS_I ( inode ) ;
struct list_head * clist ;
clist = pnfs_choose_commit_list ( req , lseg ) ;
spin_lock ( & inode - > i_lock ) ;
set_bit ( PG_CLEAN , & ( req ) - > wb_flags ) ;
radix_tree_tag_set ( & nfsi - > nfs_page_tree ,
req - > wb_index ,
NFS_PAGE_TAG_COMMIT ) ;
nfs_list_add_request ( req , clist ) ;
nfsi - > ncommit + + ;
spin_unlock ( & inode - > i_lock ) ;
pnfs_mark_request_commit ( req , lseg ) ;
inc_zone_page_state ( req - > wb_page , NR_UNSTABLE_NFS ) ;
inc_bdi_stat ( req - > wb_page - > mapping - > backing_dev_info , BDI_RECLAIMABLE ) ;
__mark_inode_dirty ( inode , I_DIRTY_DATASYNC ) ;
}
static int
static void
nfs_clear_page_commit ( struct page * page )
{
dec_zone_page_state ( page , NR_UNSTABLE_NFS ) ;
dec_bdi_stat ( page - > mapping - > backing_dev_info , BDI_RECLAIMABLE ) ;
}
static struct pnfs_layout_segment *
nfs_clear_request_commit ( struct nfs_page * req )
{
struct page * page = req - > wb_page ;
struct pnfs_layout_segment * lseg = NULL ;
if ( test_and_clear_bit ( PG_CLEAN , & ( req ) - > wb_flags ) ) {
dec_zone_page_state ( page , NR_UNSTABLE_NFS ) ;
dec_bdi_stat ( page - > mapping - > backing_dev_info , BDI_RECLAIMABLE ) ;
return 1 ;
nfs_clear_page_commit ( req - > wb_page ) ;
lseg = pnfs_clear_request_commit ( req ) ;
NFS_I ( req - > wb_context - > dentry - > d_inode ) - > ncommit - - ;
list_del ( & req - > wb_list ) ;
}
return 0 ;
return lseg ;
}
static inline
@ -494,10 +495,10 @@ nfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg)
{
}
static inline int
static inline struct pnfs_layout_segment *
nfs_clear_request_commit ( struct nfs_page * req )
{
return 0 ;
return NULL ;
}
static inline
@ -518,46 +519,67 @@ int nfs_reschedule_unstable_write(struct nfs_page *req,
static int
nfs_need_commit ( struct nfs_inode * nfsi )
{
return radix_tree_tagged ( & nfsi - > nfs_page_tree , NFS_PAGE_TAG_COMMIT ) ;
return nfsi - > ncommit > 0 ;
}
/* i_lock held by caller */
int
nfs_scan_commit_list ( struct list_head * src , struct list_head * dst , int max )
{
struct nfs_page * req , * tmp ;
int ret = 0 ;
list_for_each_entry_safe ( req , tmp , src , wb_list ) {
if ( nfs_lock_request_dontget ( req ) ) {
kref_get ( & req - > wb_kref ) ;
list_move_tail ( & req - > wb_list , dst ) ;
clear_bit ( PG_CLEAN , & ( req ) - > wb_flags ) ;
ret + + ;
if ( ret = = max )
break ;
}
}
return ret ;
}
EXPORT_SYMBOL_GPL ( nfs_scan_commit_list ) ;
/*
* nfs_scan_commit - Scan an inode for commit requests
* @ inode : NFS inode to scan
* @ dst : destination list
* @ idx_start : lower bound of page - > index to scan .
* @ npages : idx_start + npages sets the upper bound to scan .
*
* Moves requests from the inode ' s ' commit ' request list .
* The requests are * not * checked to ensure that they form a contiguous set .
*/
static int
nfs_scan_commit ( struct inode * inode , struct list_head * dst , pgoff_t idx_start , unsigned int npages )
nfs_scan_commit ( struct inode * inode , struct list_head * dst )
{
struct nfs_inode * nfsi = NFS_I ( inode ) ;
int ret ;
if ( ! nfs_need_commit ( nfsi ) )
return 0 ;
int ret = 0 ;
spin_lock ( & inode - > i_lock ) ;
ret = nfs_scan_list ( nfsi , dst , idx_start , npages , NFS_PAGE_TAG_COMMIT ) ;
if ( ret > 0 )
if ( nfsi - > ncommit > 0 ) {
int pnfs_ret ;
ret = nfs_scan_commit_list ( & nfsi - > commit_list , dst , INT_MAX ) ;
pnfs_ret = pnfs_scan_commit_lists ( inode , INT_MAX - ret ) ;
if ( pnfs_ret ) {
ret + = pnfs_ret ;
set_bit ( NFS_INO_PNFS_COMMIT , & nfsi - > flags ) ;
}
nfsi - > ncommit - = ret ;
}
spin_unlock ( & inode - > i_lock ) ;
if ( nfs_need_commit ( NFS_I ( inode ) ) )
__mark_inode_dirty ( inode , I_DIRTY_DATASYNC ) ;
return ret ;
}
# else
static inline int nfs_need_commit ( struct nfs_inode * nfsi )
{
return 0 ;
}
static inline int nfs_scan_commit ( struct inode * inode , struct list_head * dst , pgoff_t idx_start , unsigned int npages )
static inline int nfs_scan_commit ( struct inode * inode , struct list_head * dst )
{
return 0 ;
}
@ -579,6 +601,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
unsigned int rqend ;
unsigned int end ;
int error ;
struct pnfs_layout_segment * lseg = NULL ;
if ( ! PagePrivate ( page ) )
return NULL ;
@ -614,12 +637,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
spin_lock ( & inode - > i_lock ) ;
}
if ( nfs_clear_request_commit ( req ) & &
radix_tree_tag_clear ( & NFS_I ( inode ) - > nfs_page_tree ,
req - > wb_index , NFS_PAGE_TAG_COMMIT ) ! = NULL ) {
NFS_I ( inode ) - > ncommit - - ;
pnfs_clear_request_commit ( req ) ;
}
lseg = nfs_clear_request_commit ( req ) ;
/* Okay, the request matches. Update the region */
if ( offset < req - > wb_offset ) {
@ -632,6 +650,7 @@ static struct nfs_page *nfs_try_to_update_request(struct inode *inode,
req - > wb_bytes = rqend - req - > wb_offset ;
out_unlock :
spin_unlock ( & inode - > i_lock ) ;
put_lseg ( lseg ) ;
return req ;
out_flushme :
spin_unlock ( & inode - > i_lock ) ;
@ -653,7 +672,6 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
{
struct inode * inode = page - > mapping - > host ;
struct nfs_page * req ;
int error ;
req = nfs_try_to_update_request ( inode , page , offset , bytes ) ;
if ( req ! = NULL )
@ -661,11 +679,7 @@ static struct nfs_page * nfs_setup_write_request(struct nfs_open_context* ctx,
req = nfs_create_request ( ctx , inode , page , offset , bytes ) ;
if ( IS_ERR ( req ) )
goto out ;
error = nfs_inode_add_request ( inode , req ) ;
if ( error ! = 0 ) {
nfs_release_request ( req ) ;
req = ERR_PTR ( error ) ;
}
nfs_inode_add_request ( inode , req ) ;
out :
return req ;
}
@ -1458,7 +1472,7 @@ void nfs_commit_release_pages(struct nfs_write_data *data)
while ( ! list_empty ( & data - > pages ) ) {
req = nfs_list_entry ( data - > pages . next ) ;
nfs_list_remove_request ( req ) ;
nfs_clear_request _commit ( req ) ;
nfs_clear_page _commit ( req - > wb_page ) ;
dprintk ( " NFS: commit (%s/%lld %d@%lld) " ,
req - > wb_context - > dentry - > d_sb - > s_id ,
@ -1515,7 +1529,7 @@ int nfs_commit_inode(struct inode *inode, int how)
res = nfs_commit_set_lock ( NFS_I ( inode ) , may_wait ) ;
if ( res < = 0 )
goto out_mark_dirty ;
res = nfs_scan_commit ( inode , & head , 0 , 0 ) ;
res = nfs_scan_commit ( inode , & head ) ;
if ( res ) {
int error ;