@ -46,17 +46,22 @@ void nfs_pgheader_init(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header * hdr ,
void ( * release ) ( struct nfs_pgio_header * hdr ) )
{
hdr - > req = nfs_list_entry ( desc - > pg_list . next ) ;
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
hdr - > req = nfs_list_entry ( mirror - > pg_list . next ) ;
hdr - > inode = desc - > pg_inode ;
hdr - > cred = hdr - > req - > wb_context - > cred ;
hdr - > io_start = req_offset ( hdr - > req ) ;
hdr - > good_bytes = desc - > pg_count ;
hdr - > good_bytes = mirror - > pg_count ;
hdr - > dreq = desc - > pg_dreq ;
hdr - > layout_private = desc - > pg_layout_private ;
hdr - > release = release ;
hdr - > completion_ops = desc - > pg_completion_ops ;
if ( hdr - > completion_ops - > init_hdr )
hdr - > completion_ops - > init_hdr ( hdr ) ;
hdr - > pgio_mirror_idx = desc - > pg_mirror_idx ;
}
EXPORT_SYMBOL_GPL ( nfs_pgheader_init ) ;
@ -480,7 +485,10 @@ nfs_wait_on_request(struct nfs_page *req)
size_t nfs_generic_pg_test ( struct nfs_pageio_descriptor * desc ,
struct nfs_page * prev , struct nfs_page * req )
{
if ( desc - > pg_count > desc - > pg_bsize ) {
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
if ( mirror - > pg_count > mirror - > pg_bsize ) {
/* should never happen */
WARN_ON_ONCE ( 1 ) ;
return 0 ;
@ -490,11 +498,11 @@ size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc,
* Limit the request size so that we can still allocate a page array
* for it without upsetting the slab allocator .
*/
if ( ( ( desc - > pg_count + req - > wb_bytes ) > > PAGE_SHIFT ) *
if ( ( ( mirror - > pg_count + req - > wb_bytes ) > > PAGE_SHIFT ) *
sizeof ( struct page ) > PAGE_SIZE )
return 0 ;
return min ( desc - > pg_bsize - desc - > pg_count , ( size_t ) req - > wb_bytes ) ;
return min ( mirror - > pg_bsize - mirror - > pg_count , ( size_t ) req - > wb_bytes ) ;
}
EXPORT_SYMBOL_GPL ( nfs_generic_pg_test ) ;
@ -651,10 +659,18 @@ EXPORT_SYMBOL_GPL(nfs_initiate_pgio);
static int nfs_pgio_error ( struct nfs_pageio_descriptor * desc ,
struct nfs_pgio_header * hdr )
{
struct nfs_pgio_mirror * mirror ;
u32 midx ;
set_bit ( NFS_IOHDR_REDO , & hdr - > flags ) ;
nfs_pgio_data_destroy ( hdr ) ;
hdr - > completion_ops - > completion ( hdr ) ;
desc - > pg_completion_ops - > error_cleanup ( & desc - > pg_list ) ;
/* TODO: Make sure it's right to clean up all mirrors here
* and not just hdr - > pgio_mirror_idx */
for ( midx = 0 ; midx < desc - > pg_mirror_count ; midx + + ) {
mirror = & desc - > pg_mirrors [ midx ] ;
desc - > pg_completion_ops - > error_cleanup ( & mirror - > pg_list ) ;
}
return - ENOMEM ;
}
@ -671,6 +687,17 @@ static void nfs_pgio_release(void *calldata)
hdr - > completion_ops - > completion ( hdr ) ;
}
static void nfs_pageio_mirror_init ( struct nfs_pgio_mirror * mirror ,
unsigned int bsize )
{
INIT_LIST_HEAD ( & mirror - > pg_list ) ;
mirror - > pg_bytes_written = 0 ;
mirror - > pg_count = 0 ;
mirror - > pg_bsize = bsize ;
mirror - > pg_base = 0 ;
mirror - > pg_recoalesce = 0 ;
}
/**
* nfs_pageio_init - initialise a page io descriptor
* @ desc : pointer to descriptor
@ -687,13 +714,10 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
size_t bsize ,
int io_flags )
{
INIT_LIST_HEAD ( & desc - > pg_list ) ;
desc - > pg_bytes_written = 0 ;
desc - > pg_count = 0 ;
desc - > pg_bsize = bsize ;
desc - > pg_base = 0 ;
struct nfs_pgio_mirror * new ;
int i ;
desc - > pg_moreio = 0 ;
desc - > pg_recoalesce = 0 ;
desc - > pg_inode = inode ;
desc - > pg_ops = pg_ops ;
desc - > pg_completion_ops = compl_ops ;
@ -703,6 +727,26 @@ void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
desc - > pg_lseg = NULL ;
desc - > pg_dreq = NULL ;
desc - > pg_layout_private = NULL ;
desc - > pg_bsize = bsize ;
desc - > pg_mirror_count = 1 ;
desc - > pg_mirror_idx = 0 ;
if ( pg_ops - > pg_get_mirror_count ) {
/* until we have a request, we don't have an lseg and no
* idea how many mirrors there will be */
new = kcalloc ( NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX ,
sizeof ( struct nfs_pgio_mirror ) , GFP_KERNEL ) ;
desc - > pg_mirrors_dynamic = new ;
desc - > pg_mirrors = new ;
for ( i = 0 ; i < NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX ; i + + )
nfs_pageio_mirror_init ( & desc - > pg_mirrors [ i ] , bsize ) ;
} else {
desc - > pg_mirrors_dynamic = NULL ;
desc - > pg_mirrors = desc - > pg_mirrors_static ;
nfs_pageio_mirror_init ( & desc - > pg_mirrors [ 0 ] , bsize ) ;
}
}
EXPORT_SYMBOL_GPL ( nfs_pageio_init ) ;
@ -738,14 +782,16 @@ static void nfs_pgio_result(struct rpc_task *task, void *calldata)
int nfs_generic_pgio ( struct nfs_pageio_descriptor * desc ,
struct nfs_pgio_header * hdr )
{
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
struct nfs_page * req ;
struct page * * pages ,
* last_page ;
struct list_head * head = & desc - > pg_list ;
struct list_head * head = & mirror - > pg_list ;
struct nfs_commit_info cinfo ;
unsigned int pagecount , pageused ;
pagecount = nfs_page_array_len ( desc - > pg_base , desc - > pg_count ) ;
pagecount = nfs_page_array_len ( mirror - > pg_base , mirror - > pg_count ) ;
if ( ! nfs_pgarray_set ( & hdr - > page_array , pagecount ) )
return nfs_pgio_error ( desc , hdr ) ;
@ -773,7 +819,7 @@ int nfs_generic_pgio(struct nfs_pageio_descriptor *desc,
desc - > pg_ioflags & = ~ FLUSH_COND_STABLE ;
/* Set up the argument struct */
nfs_pgio_rpcsetup ( hdr , desc - > pg_count , 0 , desc - > pg_ioflags , & cinfo ) ;
nfs_pgio_rpcsetup ( hdr , mirror - > pg_count , 0 , desc - > pg_ioflags , & cinfo ) ;
desc - > pg_rpc_callops = & nfs_pgio_common_ops ;
return 0 ;
}
@ -781,12 +827,17 @@ EXPORT_SYMBOL_GPL(nfs_generic_pgio);
static int nfs_generic_pg_pgios ( struct nfs_pageio_descriptor * desc )
{
struct nfs_pgio_mirror * mirror ;
struct nfs_pgio_header * hdr ;
int ret ;
mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
hdr = nfs_pgio_header_alloc ( desc - > pg_rw_ops ) ;
if ( ! hdr ) {
desc - > pg_completion_ops - > error_cleanup ( & desc - > pg_list ) ;
/* TODO: make sure this is right with mirroring - or
* should it back out all mirrors ? */
desc - > pg_completion_ops - > error_cleanup ( & mirror - > pg_list ) ;
return - ENOMEM ;
}
nfs_pgheader_init ( desc , hdr , nfs_pgio_header_free ) ;
@ -801,6 +852,49 @@ static int nfs_generic_pg_pgios(struct nfs_pageio_descriptor *desc)
return ret ;
}
/*
* nfs_pageio_setup_mirroring - determine if mirroring is to be used
* by calling the pg_get_mirror_count op
*/
static int nfs_pageio_setup_mirroring ( struct nfs_pageio_descriptor * pgio ,
struct nfs_page * req )
{
int mirror_count = 1 ;
if ( ! pgio - > pg_ops - > pg_get_mirror_count )
return 0 ;
mirror_count = pgio - > pg_ops - > pg_get_mirror_count ( pgio , req ) ;
if ( ! mirror_count | | mirror_count > NFS_PAGEIO_DESCRIPTOR_MIRROR_MAX )
return - EINVAL ;
if ( WARN_ON_ONCE ( ! pgio - > pg_mirrors_dynamic ) )
return - EINVAL ;
pgio - > pg_mirror_count = mirror_count ;
return 0 ;
}
/*
* nfs_pageio_stop_mirroring - stop using mirroring ( set mirror count to 1 )
*/
void nfs_pageio_stop_mirroring ( struct nfs_pageio_descriptor * pgio )
{
pgio - > pg_mirror_count = 1 ;
pgio - > pg_mirror_idx = 0 ;
}
static void nfs_pageio_cleanup_mirroring ( struct nfs_pageio_descriptor * pgio )
{
pgio - > pg_mirror_count = 1 ;
pgio - > pg_mirror_idx = 0 ;
pgio - > pg_mirrors = pgio - > pg_mirrors_static ;
kfree ( pgio - > pg_mirrors_dynamic ) ;
pgio - > pg_mirrors_dynamic = NULL ;
}
static bool nfs_match_open_context ( const struct nfs_open_context * ctx1 ,
const struct nfs_open_context * ctx2 )
{
@ -867,19 +961,22 @@ static bool nfs_can_coalesce_requests(struct nfs_page *prev,
static int nfs_pageio_do_add_request ( struct nfs_pageio_descriptor * desc ,
struct nfs_page * req )
{
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
struct nfs_page * prev = NULL ;
if ( desc - > pg_count ! = 0 ) {
prev = nfs_list_entry ( desc - > pg_list . prev ) ;
if ( mirror - > pg_count ! = 0 ) {
prev = nfs_list_entry ( mirror - > pg_list . prev ) ;
} else {
if ( desc - > pg_ops - > pg_init )
desc - > pg_ops - > pg_init ( desc , req ) ;
desc - > pg_base = req - > wb_pgbase ;
mirror - > pg_base = req - > wb_pgbase ;
}
if ( ! nfs_can_coalesce_requests ( prev , req , desc ) )
return 0 ;
nfs_list_remove_request ( req ) ;
nfs_list_add_request ( req , & desc - > pg_list ) ;
desc - > pg_count + = req - > wb_bytes ;
nfs_list_add_request ( req , & mirror - > pg_list ) ;
mirror - > pg_count + = req - > wb_bytes ;
return 1 ;
}
@ -888,16 +985,19 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
*/
static void nfs_pageio_doio ( struct nfs_pageio_descriptor * desc )
{
if ( ! list_empty ( & desc - > pg_list ) ) {
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
if ( ! list_empty ( & mirror - > pg_list ) ) {
int error = desc - > pg_ops - > pg_doio ( desc ) ;
if ( error < 0 )
desc - > pg_error = error ;
else
desc - > pg_bytes_written + = desc - > pg_count ;
mirror - > pg_bytes_written + = mirror - > pg_count ;
}
if ( list_empty ( & desc - > pg_list ) ) {
desc - > pg_count = 0 ;
desc - > pg_base = 0 ;
if ( list_empty ( & mirror - > pg_list ) ) {
mirror - > pg_count = 0 ;
mirror - > pg_base = 0 ;
}
}
@ -915,10 +1015,14 @@ static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
static int __nfs_pageio_add_request ( struct nfs_pageio_descriptor * desc ,
struct nfs_page * req )
{
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
struct nfs_page * subreq ;
unsigned int bytes_left = 0 ;
unsigned int offset , pgbase ;
WARN_ON_ONCE ( desc - > pg_mirror_idx > = desc - > pg_mirror_count ) ;
nfs_page_group_lock ( req , false ) ;
subreq = req ;
@ -938,7 +1042,7 @@ static int __nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
nfs_pageio_doio ( desc ) ;
if ( desc - > pg_error < 0 )
return 0 ;
if ( desc - > pg_recoalesce )
if ( mirror - > pg_recoalesce )
return 0 ;
/* retry add_request for this subreq */
nfs_page_group_lock ( req , false ) ;
@ -976,14 +1080,16 @@ err_ptr:
static int nfs_do_recoalesce ( struct nfs_pageio_descriptor * desc )
{
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ desc - > pg_mirror_idx ] ;
LIST_HEAD ( head ) ;
do {
list_splice_init ( & desc - > pg_list , & head ) ;
desc - > pg_bytes_written - = desc - > pg_count ;
desc - > pg_count = 0 ;
desc - > pg_base = 0 ;
desc - > pg_recoalesce = 0 ;
list_splice_init ( & mirror - > pg_list , & head ) ;
mirror - > pg_bytes_written - = mirror - > pg_count ;
mirror - > pg_count = 0 ;
mirror - > pg_base = 0 ;
mirror - > pg_recoalesce = 0 ;
desc - > pg_moreio = 0 ;
while ( ! list_empty ( & head ) ) {
@ -997,11 +1103,11 @@ static int nfs_do_recoalesce(struct nfs_pageio_descriptor *desc)
return 0 ;
break ;
}
} while ( desc - > pg_recoalesce ) ;
} while ( mirror - > pg_recoalesce ) ;
return 1 ;
}
int nfs_pageio_add_request ( struct nfs_pageio_descriptor * desc ,
static int nfs_pageio_add_request_mirror ( struct nfs_pageio_descriptor * desc ,
struct nfs_page * req )
{
int ret ;
@ -1014,9 +1120,78 @@ int nfs_pageio_add_request(struct nfs_pageio_descriptor *desc,
break ;
ret = nfs_do_recoalesce ( desc ) ;
} while ( ret ) ;
return ret ;
}
int nfs_pageio_add_request ( struct nfs_pageio_descriptor * desc ,
struct nfs_page * req )
{
u32 midx ;
unsigned int pgbase , offset , bytes ;
struct nfs_page * dupreq , * lastreq ;
pgbase = req - > wb_pgbase ;
offset = req - > wb_offset ;
bytes = req - > wb_bytes ;
nfs_pageio_setup_mirroring ( desc , req ) ;
for ( midx = 0 ; midx < desc - > pg_mirror_count ; midx + + ) {
if ( midx ) {
nfs_page_group_lock ( req , false ) ;
/* find the last request */
for ( lastreq = req - > wb_head ;
lastreq - > wb_this_page ! = req - > wb_head ;
lastreq = lastreq - > wb_this_page )
;
dupreq = nfs_create_request ( req - > wb_context ,
req - > wb_page , lastreq , pgbase , bytes ) ;
if ( IS_ERR ( dupreq ) ) {
nfs_page_group_unlock ( req ) ;
return 0 ;
}
nfs_lock_request ( dupreq ) ;
nfs_page_group_unlock ( req ) ;
dupreq - > wb_offset = offset ;
dupreq - > wb_index = req - > wb_index ;
} else
dupreq = req ;
desc - > pg_mirror_idx = midx ;
if ( ! nfs_pageio_add_request_mirror ( desc , dupreq ) )
return 0 ;
}
return 1 ;
}
/*
* nfs_pageio_complete_mirror - Complete I / O on the current mirror of an
* nfs_pageio_descriptor
* @ desc : pointer to io descriptor
*/
static void nfs_pageio_complete_mirror ( struct nfs_pageio_descriptor * desc ,
u32 mirror_idx )
{
struct nfs_pgio_mirror * mirror = & desc - > pg_mirrors [ mirror_idx ] ;
u32 restore_idx = desc - > pg_mirror_idx ;
desc - > pg_mirror_idx = mirror_idx ;
for ( ; ; ) {
nfs_pageio_doio ( desc ) ;
if ( ! mirror - > pg_recoalesce )
break ;
if ( ! nfs_do_recoalesce ( desc ) )
break ;
}
desc - > pg_mirror_idx = restore_idx ;
}
/*
* nfs_pageio_resend - Transfer requests to new descriptor and resend
* @ hdr - the pgio header to move request from
@ -1055,16 +1230,14 @@ EXPORT_SYMBOL_GPL(nfs_pageio_resend);
*/
void nfs_pageio_complete ( struct nfs_pageio_descriptor * desc )
{
for ( ; ; ) {
nfs_pageio_doio ( desc ) ;
if ( ! desc - > pg_recoalesce )
break ;
if ( ! nfs_do_recoalesce ( desc ) )
break ;
}
u32 midx ;
for ( midx = 0 ; midx < desc - > pg_mirror_count ; midx + + )
nfs_pageio_complete_mirror ( desc , midx ) ;
if ( desc - > pg_ops - > pg_cleanup )
desc - > pg_ops - > pg_cleanup ( desc ) ;
nfs_pageio_cleanup_mirroring ( desc ) ;
}
/**
@ -1080,10 +1253,17 @@ void nfs_pageio_complete(struct nfs_pageio_descriptor *desc)
*/
void nfs_pageio_cond_complete ( struct nfs_pageio_descriptor * desc , pgoff_t index )
{
if ( ! list_empty ( & desc - > pg_list ) ) {
struct nfs_page * prev = nfs_list_entry ( desc - > pg_list . prev ) ;
if ( index ! = prev - > wb_index + 1 )
nfs_pageio_complete ( desc ) ;
struct nfs_pgio_mirror * mirror ;
struct nfs_page * prev ;
u32 midx ;
for ( midx = 0 ; midx < desc - > pg_mirror_count ; midx + + ) {
mirror = & desc - > pg_mirrors [ midx ] ;
if ( ! list_empty ( & mirror - > pg_list ) ) {
prev = nfs_list_entry ( mirror - > pg_list . prev ) ;
if ( index ! = prev - > wb_index + 1 )
nfs_pageio_complete_mirror ( desc , midx ) ;
}
}
}