2009-04-07 06:01:36 +04:00
/*
* segbuf . c - NILFS segment buffer
*
* Copyright ( C ) 2005 - 2008 Nippon Telegraph and Telephone Corporation .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
* Written by Ryusuke Konishi < ryusuke @ osrg . net >
*
*/
# include <linux/buffer_head.h>
# include <linux/writeback.h>
# include <linux/crc32.h>
2009-11-28 19:17:31 +03:00
# include <linux/backing-dev.h>
2009-04-07 06:01:36 +04:00
# include "page.h"
# include "segbuf.h"
2009-11-28 19:17:31 +03:00
struct nilfs_write_info {
struct the_nilfs * nilfs ;
struct bio * bio ;
int start , end ; /* The region to be submitted */
int rest_blocks ;
int max_pages ;
int nr_vecs ;
sector_t blocknr ;
} ;
2009-04-07 06:01:36 +04:00
static struct kmem_cache * nilfs_segbuf_cachep ;
static void nilfs_segbuf_init_once ( void * obj )
{
memset ( obj , 0 , sizeof ( struct nilfs_segment_buffer ) ) ;
}
int __init nilfs_init_segbuf_cache ( void )
{
nilfs_segbuf_cachep =
kmem_cache_create ( " nilfs2_segbuf_cache " ,
sizeof ( struct nilfs_segment_buffer ) ,
0 , SLAB_RECLAIM_ACCOUNT ,
nilfs_segbuf_init_once ) ;
return ( nilfs_segbuf_cachep = = NULL ) ? - ENOMEM : 0 ;
}
void nilfs_destroy_segbuf_cache ( void )
{
kmem_cache_destroy ( nilfs_segbuf_cachep ) ;
}
struct nilfs_segment_buffer * nilfs_segbuf_new ( struct super_block * sb )
{
struct nilfs_segment_buffer * segbuf ;
segbuf = kmem_cache_alloc ( nilfs_segbuf_cachep , GFP_NOFS ) ;
if ( unlikely ( ! segbuf ) )
return NULL ;
segbuf - > sb_super = sb ;
INIT_LIST_HEAD ( & segbuf - > sb_list ) ;
INIT_LIST_HEAD ( & segbuf - > sb_segsum_buffers ) ;
INIT_LIST_HEAD ( & segbuf - > sb_payload_buffers ) ;
2009-11-24 19:04:21 +03:00
init_completion ( & segbuf - > sb_bio_event ) ;
atomic_set ( & segbuf - > sb_err , 0 ) ;
segbuf - > sb_nbio = 0 ;
2009-04-07 06:01:36 +04:00
return segbuf ;
}
void nilfs_segbuf_free ( struct nilfs_segment_buffer * segbuf )
{
kmem_cache_free ( nilfs_segbuf_cachep , segbuf ) ;
}
2009-04-07 06:01:58 +04:00
void nilfs_segbuf_map ( struct nilfs_segment_buffer * segbuf , __u64 segnum ,
2009-04-07 06:01:36 +04:00
unsigned long offset , struct the_nilfs * nilfs )
{
segbuf - > sb_segnum = segnum ;
nilfs_get_segment_range ( nilfs , segnum , & segbuf - > sb_fseg_start ,
& segbuf - > sb_fseg_end ) ;
segbuf - > sb_pseg_start = segbuf - > sb_fseg_start + offset ;
segbuf - > sb_rest_blocks =
segbuf - > sb_fseg_end - segbuf - > sb_pseg_start + 1 ;
}
void nilfs_segbuf_set_next_segnum ( struct nilfs_segment_buffer * segbuf ,
__u64 nextnum , struct the_nilfs * nilfs )
{
segbuf - > sb_nextnum = nextnum ;
segbuf - > sb_sum . next = nilfs_get_segment_start_blocknr ( nilfs , nextnum ) ;
}
int nilfs_segbuf_extend_segsum ( struct nilfs_segment_buffer * segbuf )
{
struct buffer_head * bh ;
bh = sb_getblk ( segbuf - > sb_super ,
segbuf - > sb_pseg_start + segbuf - > sb_sum . nsumblk ) ;
if ( unlikely ( ! bh ) )
return - ENOMEM ;
nilfs_segbuf_add_segsum_buffer ( segbuf , bh ) ;
return 0 ;
}
int nilfs_segbuf_extend_payload ( struct nilfs_segment_buffer * segbuf ,
struct buffer_head * * bhp )
{
struct buffer_head * bh ;
bh = sb_getblk ( segbuf - > sb_super ,
segbuf - > sb_pseg_start + segbuf - > sb_sum . nblocks ) ;
if ( unlikely ( ! bh ) )
return - ENOMEM ;
nilfs_segbuf_add_payload_buffer ( segbuf , bh ) ;
* bhp = bh ;
return 0 ;
}
int nilfs_segbuf_reset ( struct nilfs_segment_buffer * segbuf , unsigned flags ,
time_t ctime )
{
int err ;
segbuf - > sb_sum . nblocks = segbuf - > sb_sum . nsumblk = 0 ;
err = nilfs_segbuf_extend_segsum ( segbuf ) ;
if ( unlikely ( err ) )
return err ;
segbuf - > sb_sum . flags = flags ;
segbuf - > sb_sum . sumbytes = sizeof ( struct nilfs_segment_summary ) ;
segbuf - > sb_sum . nfinfo = segbuf - > sb_sum . nfileblk = 0 ;
segbuf - > sb_sum . ctime = ctime ;
return 0 ;
}
/*
* Setup segument summary
*/
void nilfs_segbuf_fill_in_segsum ( struct nilfs_segment_buffer * segbuf )
{
struct nilfs_segment_summary * raw_sum ;
struct buffer_head * bh_sum ;
bh_sum = list_entry ( segbuf - > sb_segsum_buffers . next ,
struct buffer_head , b_assoc_buffers ) ;
raw_sum = ( struct nilfs_segment_summary * ) bh_sum - > b_data ;
raw_sum - > ss_magic = cpu_to_le32 ( NILFS_SEGSUM_MAGIC ) ;
raw_sum - > ss_bytes = cpu_to_le16 ( sizeof ( * raw_sum ) ) ;
raw_sum - > ss_flags = cpu_to_le16 ( segbuf - > sb_sum . flags ) ;
raw_sum - > ss_seq = cpu_to_le64 ( segbuf - > sb_sum . seg_seq ) ;
raw_sum - > ss_create = cpu_to_le64 ( segbuf - > sb_sum . ctime ) ;
raw_sum - > ss_next = cpu_to_le64 ( segbuf - > sb_sum . next ) ;
raw_sum - > ss_nblocks = cpu_to_le32 ( segbuf - > sb_sum . nblocks ) ;
raw_sum - > ss_nfinfo = cpu_to_le32 ( segbuf - > sb_sum . nfinfo ) ;
raw_sum - > ss_sumbytes = cpu_to_le32 ( segbuf - > sb_sum . sumbytes ) ;
raw_sum - > ss_pad = 0 ;
}
/*
* CRC calculation routines
*/
void nilfs_segbuf_fill_in_segsum_crc ( struct nilfs_segment_buffer * segbuf ,
u32 seed )
{
struct buffer_head * bh ;
struct nilfs_segment_summary * raw_sum ;
unsigned long size , bytes = segbuf - > sb_sum . sumbytes ;
u32 crc ;
bh = list_entry ( segbuf - > sb_segsum_buffers . next , struct buffer_head ,
b_assoc_buffers ) ;
raw_sum = ( struct nilfs_segment_summary * ) bh - > b_data ;
size = min_t ( unsigned long , bytes , bh - > b_size ) ;
crc = crc32_le ( seed ,
( unsigned char * ) raw_sum +
sizeof ( raw_sum - > ss_datasum ) + sizeof ( raw_sum - > ss_sumsum ) ,
size - ( sizeof ( raw_sum - > ss_datasum ) +
sizeof ( raw_sum - > ss_sumsum ) ) ) ;
list_for_each_entry_continue ( bh , & segbuf - > sb_segsum_buffers ,
b_assoc_buffers ) {
bytes - = size ;
size = min_t ( unsigned long , bytes , bh - > b_size ) ;
crc = crc32_le ( crc , bh - > b_data , size ) ;
}
raw_sum - > ss_sumsum = cpu_to_le32 ( crc ) ;
}
void nilfs_segbuf_fill_in_data_crc ( struct nilfs_segment_buffer * segbuf ,
u32 seed )
{
struct buffer_head * bh ;
struct nilfs_segment_summary * raw_sum ;
void * kaddr ;
u32 crc ;
bh = list_entry ( segbuf - > sb_segsum_buffers . next , struct buffer_head ,
b_assoc_buffers ) ;
raw_sum = ( struct nilfs_segment_summary * ) bh - > b_data ;
crc = crc32_le ( seed ,
( unsigned char * ) raw_sum + sizeof ( raw_sum - > ss_datasum ) ,
bh - > b_size - sizeof ( raw_sum - > ss_datasum ) ) ;
list_for_each_entry_continue ( bh , & segbuf - > sb_segsum_buffers ,
b_assoc_buffers ) {
crc = crc32_le ( crc , bh - > b_data , bh - > b_size ) ;
}
list_for_each_entry ( bh , & segbuf - > sb_payload_buffers , b_assoc_buffers ) {
kaddr = kmap_atomic ( bh - > b_page , KM_USER0 ) ;
crc = crc32_le ( crc , kaddr + bh_offset ( bh ) , bh - > b_size ) ;
kunmap_atomic ( kaddr , KM_USER0 ) ;
}
raw_sum - > ss_datasum = cpu_to_le32 ( crc ) ;
}
void nilfs_release_buffers ( struct list_head * list )
{
struct buffer_head * bh , * n ;
list_for_each_entry_safe ( bh , n , list , b_assoc_buffers ) {
list_del_init ( & bh - > b_assoc_buffers ) ;
if ( buffer_nilfs_allocated ( bh ) ) {
struct page * clone_page = bh - > b_page ;
/* remove clone page */
brelse ( bh ) ;
page_cache_release ( clone_page ) ; /* for each bh */
if ( page_count ( clone_page ) < = 2 ) {
lock_page ( clone_page ) ;
nilfs_free_private_page ( clone_page ) ;
}
continue ;
}
brelse ( bh ) ;
}
}
/*
* BIO operations
*/
static void nilfs_end_bio_write ( struct bio * bio , int err )
{
const int uptodate = test_bit ( BIO_UPTODATE , & bio - > bi_flags ) ;
2009-11-24 19:04:21 +03:00
struct nilfs_segment_buffer * segbuf = bio - > bi_private ;
2009-04-07 06:01:36 +04:00
if ( err = = - EOPNOTSUPP ) {
set_bit ( BIO_EOPNOTSUPP , & bio - > bi_flags ) ;
bio_put ( bio ) ;
/* to be detected by submit_seg_bio() */
}
if ( ! uptodate )
2009-11-24 19:04:21 +03:00
atomic_inc ( & segbuf - > sb_err ) ;
2009-04-07 06:01:36 +04:00
bio_put ( bio ) ;
2009-11-24 19:04:21 +03:00
complete ( & segbuf - > sb_bio_event ) ;
2009-04-07 06:01:36 +04:00
}
2009-11-24 19:04:21 +03:00
static int nilfs_segbuf_submit_bio ( struct nilfs_segment_buffer * segbuf ,
struct nilfs_write_info * wi , int mode )
2009-04-07 06:01:36 +04:00
{
struct bio * bio = wi - > bio ;
int err ;
2009-11-28 19:17:31 +03:00
if ( segbuf - > sb_nbio > 0 & & bdi_write_congested ( wi - > nilfs - > ns_bdi ) ) {
2009-11-24 19:04:21 +03:00
wait_for_completion ( & segbuf - > sb_bio_event ) ;
segbuf - > sb_nbio - - ;
if ( unlikely ( atomic_read ( & segbuf - > sb_err ) ) ) {
2009-04-07 06:01:36 +04:00
bio_put ( bio ) ;
err = - EIO ;
goto failed ;
}
}
bio - > bi_end_io = nilfs_end_bio_write ;
2009-11-24 19:04:21 +03:00
bio - > bi_private = segbuf ;
2009-04-07 06:01:36 +04:00
bio_get ( bio ) ;
submit_bio ( mode , bio ) ;
if ( bio_flagged ( bio , BIO_EOPNOTSUPP ) ) {
bio_put ( bio ) ;
err = - EOPNOTSUPP ;
goto failed ;
}
2009-11-24 19:04:21 +03:00
segbuf - > sb_nbio + + ;
2009-04-07 06:01:36 +04:00
bio_put ( bio ) ;
wi - > bio = NULL ;
wi - > rest_blocks - = wi - > end - wi - > start ;
wi - > nr_vecs = min ( wi - > max_pages , wi - > rest_blocks ) ;
wi - > start = wi - > end ;
return 0 ;
failed :
wi - > bio = NULL ;
return err ;
}
/**
2009-11-28 19:17:31 +03:00
* nilfs_alloc_seg_bio - allocate a new bio for writing log
* @ nilfs : nilfs object
* @ start : start block number of the bio
2009-04-07 06:01:36 +04:00
* @ nr_vecs : request size of page vector .
*
* Return Value : On success , pointer to the struct bio is returned .
* On error , NULL is returned .
*/
2009-11-28 19:17:31 +03:00
static struct bio * nilfs_alloc_seg_bio ( struct the_nilfs * nilfs , sector_t start ,
2009-04-07 06:01:36 +04:00
int nr_vecs )
{
struct bio * bio ;
2009-06-19 10:25:42 +04:00
bio = bio_alloc ( GFP_NOIO , nr_vecs ) ;
2009-04-07 06:01:36 +04:00
if ( bio = = NULL ) {
while ( ! bio & & ( nr_vecs > > = 1 ) )
2009-06-19 10:25:42 +04:00
bio = bio_alloc ( GFP_NOIO , nr_vecs ) ;
2009-04-07 06:01:36 +04:00
}
if ( likely ( bio ) ) {
2009-11-28 19:17:31 +03:00
bio - > bi_bdev = nilfs - > ns_bdev ;
bio - > bi_sector = start < < ( nilfs - > ns_blocksize_bits - 9 ) ;
2009-04-07 06:01:36 +04:00
}
return bio ;
}
2009-11-28 19:17:31 +03:00
static void nilfs_segbuf_prepare_write ( struct nilfs_segment_buffer * segbuf ,
struct nilfs_write_info * wi )
2009-04-07 06:01:36 +04:00
{
wi - > bio = NULL ;
wi - > rest_blocks = segbuf - > sb_sum . nblocks ;
2009-11-28 19:17:31 +03:00
wi - > max_pages = bio_get_nr_vecs ( wi - > nilfs - > ns_bdev ) ;
2009-04-07 06:01:36 +04:00
wi - > nr_vecs = min ( wi - > max_pages , wi - > rest_blocks ) ;
wi - > start = wi - > end = 0 ;
wi - > blocknr = segbuf - > sb_pseg_start ;
}
2009-11-24 19:04:21 +03:00
static int nilfs_segbuf_submit_bh ( struct nilfs_segment_buffer * segbuf ,
struct nilfs_write_info * wi ,
struct buffer_head * bh , int mode )
2009-04-07 06:01:36 +04:00
{
int len , err ;
BUG_ON ( wi - > nr_vecs < = 0 ) ;
repeat :
if ( ! wi - > bio ) {
2009-11-28 19:17:31 +03:00
wi - > bio = nilfs_alloc_seg_bio ( wi - > nilfs , wi - > blocknr + wi - > end ,
2009-04-07 06:01:36 +04:00
wi - > nr_vecs ) ;
if ( unlikely ( ! wi - > bio ) )
return - ENOMEM ;
}
len = bio_add_page ( wi - > bio , bh - > b_page , bh - > b_size , bh_offset ( bh ) ) ;
if ( len = = bh - > b_size ) {
wi - > end + + ;
return 0 ;
}
/* bio is FULL */
2009-11-24 19:04:21 +03:00
err = nilfs_segbuf_submit_bio ( segbuf , wi , mode ) ;
2009-04-07 06:01:36 +04:00
/* never submit current bh */
if ( likely ( ! err ) )
goto repeat ;
return err ;
}
2009-11-28 19:17:31 +03:00
/**
* nilfs_segbuf_write - submit write requests of a log
* @ segbuf : buffer storing a log to be written
* @ nilfs : nilfs object
*
* Return Value : On Success , 0 is returned . On Error , one of the following
* negative error code is returned .
*
* % - EIO - I / O error
*
* % - ENOMEM - Insufficient memory available .
*/
2009-04-07 06:01:36 +04:00
int nilfs_segbuf_write ( struct nilfs_segment_buffer * segbuf ,
2009-11-28 19:17:31 +03:00
struct the_nilfs * nilfs )
2009-04-07 06:01:36 +04:00
{
2009-11-28 19:17:31 +03:00
struct nilfs_write_info wi ;
2009-04-07 06:01:36 +04:00
struct buffer_head * bh ;
2009-11-29 13:14:17 +03:00
int res = 0 , rw = WRITE ;
2009-04-07 06:01:36 +04:00
2009-11-28 19:17:31 +03:00
wi . nilfs = nilfs ;
nilfs_segbuf_prepare_write ( segbuf , & wi ) ;
2009-04-07 06:01:36 +04:00
list_for_each_entry ( bh , & segbuf - > sb_segsum_buffers , b_assoc_buffers ) {
2009-11-28 19:17:31 +03:00
res = nilfs_segbuf_submit_bh ( segbuf , & wi , bh , rw ) ;
2009-04-07 06:01:36 +04:00
if ( unlikely ( res ) )
goto failed_bio ;
}
list_for_each_entry ( bh , & segbuf - > sb_payload_buffers , b_assoc_buffers ) {
2009-11-28 19:17:31 +03:00
res = nilfs_segbuf_submit_bh ( segbuf , & wi , bh , rw ) ;
2009-04-07 06:01:36 +04:00
if ( unlikely ( res ) )
goto failed_bio ;
}
2009-11-28 19:17:31 +03:00
if ( wi . bio ) {
2009-04-07 06:01:36 +04:00
/*
* Last BIO is always sent through the following
* submission .
*/
2009-05-17 05:57:43 +04:00
rw | = ( 1 < < BIO_RW_SYNCIO ) | ( 1 < < BIO_RW_UNPLUG ) ;
2009-11-28 19:17:31 +03:00
res = nilfs_segbuf_submit_bio ( segbuf , & wi , rw ) ;
2009-04-07 06:01:36 +04:00
}
failed_bio :
2009-11-29 13:14:17 +03:00
return res ;
2009-04-07 06:01:36 +04:00
}
/**
* nilfs_segbuf_wait - wait for completion of requested BIOs
2009-11-24 19:04:21 +03:00
* @ segbuf : segment buffer
2009-04-07 06:01:36 +04:00
*
* Return Value : On Success , 0 is returned . On Error , one of the following
* negative error code is returned .
*
* % - EIO - I / O error
*/
2009-11-24 19:04:21 +03:00
int nilfs_segbuf_wait ( struct nilfs_segment_buffer * segbuf )
2009-04-07 06:01:36 +04:00
{
int err = 0 ;
2009-11-24 19:04:21 +03:00
if ( ! segbuf - > sb_nbio )
2009-04-07 06:01:36 +04:00
return 0 ;
do {
2009-11-24 19:04:21 +03:00
wait_for_completion ( & segbuf - > sb_bio_event ) ;
} while ( - - segbuf - > sb_nbio > 0 ) ;
2009-04-07 06:01:36 +04:00
2009-11-24 19:04:21 +03:00
if ( unlikely ( atomic_read ( & segbuf - > sb_err ) > 0 ) ) {
2009-04-07 06:01:36 +04:00
printk ( KERN_ERR " NILFS: IO error writing segment \n " ) ;
err = - EIO ;
}
return err ;
}