2005-04-17 02:20:36 +04:00
/*
* Copyright 2000 by Hans Reiser , licensing governed by reiserfs / README
*/
2005-07-13 07:21:28 +04:00
2005-04-17 02:20:36 +04:00
/*
* Written by Alexander Zarochentcev .
*
* The kernel part of the ( on - line ) reiserfs resizer .
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/vmalloc.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/reiserfs_fs.h>
# include <linux/reiserfs_fs_sb.h>
# include <linux/buffer_head.h>
2005-07-13 07:21:28 +04:00
int reiserfs_resize ( struct super_block * s , unsigned long block_count_new )
2005-04-17 02:20:36 +04:00
{
2005-07-13 07:21:28 +04:00
int err = 0 ;
struct reiserfs_super_block * sb ;
struct reiserfs_bitmap_info * bitmap ;
2006-10-01 10:28:42 +04:00
struct reiserfs_bitmap_info * info ;
2005-04-17 02:20:36 +04:00
struct reiserfs_bitmap_info * old_bitmap = SB_AP_BITMAP ( s ) ;
2005-07-13 07:21:28 +04:00
struct buffer_head * bh ;
2005-04-17 02:20:36 +04:00
struct reiserfs_transaction_handle th ;
unsigned int bmap_nr_new , bmap_nr ;
unsigned int block_r_new , block_r ;
2005-07-13 07:21:28 +04:00
struct reiserfs_list_bitmap * jb ;
2005-04-17 02:20:36 +04:00
struct reiserfs_list_bitmap jbitmap [ JOURNAL_NUM_BITMAPS ] ;
2005-07-13 07:21:28 +04:00
2005-04-17 02:20:36 +04:00
unsigned long int block_count , free_blocks ;
int i ;
2005-07-13 07:21:28 +04:00
int copy_size ;
2005-04-17 02:20:36 +04:00
sb = SB_DISK_SUPER_BLOCK ( s ) ;
if ( SB_BLOCK_COUNT ( s ) > = block_count_new ) {
printk ( " can \' t shrink filesystem on-line \n " ) ;
return - EINVAL ;
}
/* check the device size */
bh = sb_bread ( s , block_count_new - 1 ) ;
if ( ! bh ) {
printk ( " reiserfs_resize: can \' t read last block \n " ) ;
return - EINVAL ;
2005-07-13 07:21:28 +04:00
}
2005-04-17 02:20:36 +04:00
bforget ( bh ) ;
/* old disk layout detection; those partitions can be mounted, but
* cannot be resized */
2005-07-13 07:21:28 +04:00
if ( SB_BUFFER_WITH_SB ( s ) - > b_blocknr * SB_BUFFER_WITH_SB ( s ) - > b_size
! = REISERFS_DISK_OFFSET_IN_BYTES ) {
printk
( " reiserfs_resize: unable to resize a reiserfs without distributed bitmap (fs version < 3.5.12) \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOTSUPP ;
}
2005-07-13 07:21:28 +04:00
2005-04-17 02:20:36 +04:00
/* count used bits in last bitmap block */
2005-07-13 07:21:28 +04:00
block_r = SB_BLOCK_COUNT ( s ) - ( SB_BMAP_NR ( s ) - 1 ) * s - > s_blocksize * 8 ;
2005-04-17 02:20:36 +04:00
/* count bitmap blocks in new fs */
2005-07-13 07:21:28 +04:00
bmap_nr_new = block_count_new / ( s - > s_blocksize * 8 ) ;
2005-04-17 02:20:36 +04:00
block_r_new = block_count_new - bmap_nr_new * s - > s_blocksize * 8 ;
2005-07-13 07:21:28 +04:00
if ( block_r_new )
2005-04-17 02:20:36 +04:00
bmap_nr_new + + ;
else
block_r_new = s - > s_blocksize * 8 ;
/* save old values */
block_count = SB_BLOCK_COUNT ( s ) ;
2005-07-13 07:21:28 +04:00
bmap_nr = SB_BMAP_NR ( s ) ;
2005-04-17 02:20:36 +04:00
/* resizing of reiserfs bitmaps (journal and real), if needed */
2005-07-13 07:21:28 +04:00
if ( bmap_nr_new > bmap_nr ) {
/* reallocate journal bitmaps */
if ( reiserfs_allocate_list_bitmaps ( s , jbitmap , bmap_nr_new ) < 0 ) {
printk
( " reiserfs_resize: unable to allocate memory for journal bitmaps \n " ) ;
unlock_super ( s ) ;
return - ENOMEM ;
}
/* the new journal bitmaps are zero filled, now we copy in the bitmap
* * node pointers from the old journal bitmap structs , and then
* * transfer the new data structures into the journal struct .
* *
* * using the copy_size var below allows this code to work for
* * both shrinking and expanding the FS .
*/
copy_size = bmap_nr_new < bmap_nr ? bmap_nr_new : bmap_nr ;
copy_size =
copy_size * sizeof ( struct reiserfs_list_bitmap_node * ) ;
for ( i = 0 ; i < JOURNAL_NUM_BITMAPS ; i + + ) {
struct reiserfs_bitmap_node * * node_tmp ;
jb = SB_JOURNAL ( s ) - > j_list_bitmap + i ;
memcpy ( jbitmap [ i ] . bitmaps , jb - > bitmaps , copy_size ) ;
/* just in case vfree schedules on us, copy the new
* * pointer into the journal struct before freeing the
* * old one
*/
node_tmp = jb - > bitmaps ;
jb - > bitmaps = jbitmap [ i ] . bitmaps ;
vfree ( node_tmp ) ;
}
/* allocate additional bitmap blocks, reallocate array of bitmap
* block pointers */
bitmap =
vmalloc ( sizeof ( struct reiserfs_bitmap_info ) * bmap_nr_new ) ;
if ( ! bitmap ) {
/* Journal bitmaps are still supersized, but the memory isn't
* leaked , so I guess it ' s ok */
printk ( " reiserfs_resize: unable to allocate memory. \n " ) ;
return - ENOMEM ;
}
memset ( bitmap , 0 ,
sizeof ( struct reiserfs_bitmap_info ) * SB_BMAP_NR ( s ) ) ;
for ( i = 0 ; i < bmap_nr ; i + + )
bitmap [ i ] = old_bitmap [ i ] ;
/* This doesn't go through the journal, but it doesn't have to.
* The changes are still atomic : We ' re synced up when the journal
* transaction begins , and the new bitmaps don ' t matter if the
* transaction fails . */
for ( i = bmap_nr ; i < bmap_nr_new ; i + + ) {
2006-10-01 10:28:44 +04:00
/* don't use read_bitmap_block since it will cache
* the uninitialized bitmap */
bh = sb_bread ( s , i * s - > s_blocksize * 8 ) ;
2006-10-01 10:28:42 +04:00
memset ( bh - > b_data , 0 , sb_blocksize ( sb ) ) ;
reiserfs_test_and_set_le_bit ( 0 , bh - > b_data ) ;
2006-10-01 10:28:43 +04:00
reiserfs_cache_bitmap_metadata ( s , bh , bitmap + i ) ;
2006-10-01 10:28:42 +04:00
set_buffer_uptodate ( bh ) ;
mark_buffer_dirty ( bh ) ;
sync_dirty_buffer ( bh ) ;
2005-07-13 07:21:28 +04:00
// update bitmap_info stuff
bitmap [ i ] . first_zero_hint = 1 ;
bitmap [ i ] . free_count = sb_blocksize ( sb ) * 8 - 1 ;
2006-10-01 10:28:42 +04:00
brelse ( bh ) ;
2005-07-13 07:21:28 +04:00
}
/* free old bitmap blocks array */
SB_AP_BITMAP ( s ) = bitmap ;
vfree ( old_bitmap ) ;
2005-04-17 02:20:36 +04:00
}
2005-07-13 07:21:28 +04:00
2005-04-17 02:20:36 +04:00
/* begin transaction, if there was an error, it's fine. Yes, we have
* incorrect bitmaps now , but none of it is ever going to touch the
* disk anyway . */
err = journal_begin ( & th , s , 10 ) ;
if ( err )
2005-07-13 07:21:28 +04:00
return err ;
2005-04-17 02:20:36 +04:00
2006-10-01 10:28:42 +04:00
/* Extend old last bitmap block - new blocks have been made available */
info = SB_AP_BITMAP ( s ) + bmap_nr - 1 ;
2006-10-01 10:28:44 +04:00
bh = reiserfs_read_bitmap_block ( s , bmap_nr - 1 ) ;
if ( ! bh ) {
int jerr = journal_end ( & th , s , 10 ) ;
if ( jerr )
return jerr ;
return - EIO ;
}
2006-10-01 10:28:42 +04:00
reiserfs_prepare_for_journal ( s , bh , 1 ) ;
2005-04-17 02:20:36 +04:00
for ( i = block_r ; i < s - > s_blocksize * 8 ; i + + )
2006-10-01 10:28:42 +04:00
reiserfs_test_and_clear_le_bit ( i , bh - > b_data ) ;
info - > free_count + = s - > s_blocksize * 8 - block_r ;
if ( ! info - > first_zero_hint )
info - > first_zero_hint = block_r ;
journal_mark_dirty ( & th , s , bh ) ;
brelse ( bh ) ;
2005-04-17 02:20:36 +04:00
2006-10-01 10:28:42 +04:00
/* Correct new last bitmap block - It may not be full */
info = SB_AP_BITMAP ( s ) + bmap_nr_new - 1 ;
2006-10-01 10:28:44 +04:00
bh = reiserfs_read_bitmap_block ( s , bmap_nr_new - 1 ) ;
if ( ! bh ) {
int jerr = journal_end ( & th , s , 10 ) ;
if ( jerr )
return jerr ;
return - EIO ;
}
2005-04-17 02:20:36 +04:00
2006-10-01 10:28:42 +04:00
reiserfs_prepare_for_journal ( s , bh , 1 ) ;
2005-04-17 02:20:36 +04:00
for ( i = block_r_new ; i < s - > s_blocksize * 8 ; i + + )
2006-10-01 10:28:42 +04:00
reiserfs_test_and_set_le_bit ( i , bh - > b_data ) ;
journal_mark_dirty ( & th , s , bh ) ;
brelse ( bh ) ;
2005-07-13 07:21:28 +04:00
2006-10-01 10:28:42 +04:00
info - > free_count - = s - > s_blocksize * 8 - block_r_new ;
2005-04-17 02:20:36 +04:00
/* Extreme case where last bitmap is the only valid block in itself. */
2006-10-01 10:28:42 +04:00
if ( ! info - > free_count )
info - > first_zero_hint = 0 ;
2005-07-13 07:21:28 +04:00
/* update super */
reiserfs_prepare_for_journal ( s , SB_BUFFER_WITH_SB ( s ) , 1 ) ;
2005-04-17 02:20:36 +04:00
free_blocks = SB_FREE_BLOCKS ( s ) ;
2005-07-13 07:21:28 +04:00
PUT_SB_FREE_BLOCKS ( s ,
free_blocks + ( block_count_new - block_count -
( bmap_nr_new - bmap_nr ) ) ) ;
2005-04-17 02:20:36 +04:00
PUT_SB_BLOCK_COUNT ( s , block_count_new ) ;
PUT_SB_BMAP_NR ( s , bmap_nr_new ) ;
s - > s_dirt = 1 ;
journal_mark_dirty ( & th , s , SB_BUFFER_WITH_SB ( s ) ) ;
2005-07-13 07:21:28 +04:00
2005-04-17 02:20:36 +04:00
SB_JOURNAL ( s ) - > j_must_wait = 1 ;
return journal_end ( & th , s , 10 ) ;
}