// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) International Business Machines Corp . , 2000 - 2004
*/
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/quotaops.h>
#include <linux/blkdev.h>
#include "jfs_incore.h"
#include "jfs_filsys.h"
#include "jfs_metapage.h"
#include "jfs_dinode.h"
#include "jfs_imap.h"
#include "jfs_dmap.h"
#include "jfs_superblock.h"
#include "jfs_txnmgr.h"
#include "jfs_debug.h"
#define BITSPERPAGE (PSIZE << 3 )
#define L2MEGABYTE 20
#define MEGABYTE (1 << L2MEGABYTE)
#define MEGABYTE32 (MEGABYTE << 5 )
/* convert block number to bmap file page number */
#define BLKTODMAPN(b)\
(((b) >> 13 ) + ((b) >> 23 ) + ((b) >> 33 ) + 3 + 1 )
/*
* jfs_extendfs ( )
*
* function : extend file system ;
*
* | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - - | - - - - - - - - - - |
* file system space fsck inline log
* workspace space
*
* input :
* new LVSize : in LV blocks ( required )
* new LogSize : in LV blocks ( optional )
* new FSSize : in LV blocks ( optional )
*
* new configuration :
* 1 . set new LogSize as specified or default from new LVSize ;
* 2 . compute new FSCKSize from new LVSize ;
* 3 . set new FSSize as MIN ( FSSize , LVSize - ( LogSize + FSCKSize ) ) where
* assert ( new FSSize > = old FSSize ) ,
* i . e . , file system must not be shrunk ;
*/
int jfs_extendfs(struct super_block *sb, s64 newLVSize, int newLogSize)
{
int rc = 0 ;
struct jfs_sb_info *sbi = JFS_SBI(sb);
struct inode *ipbmap = sbi->ipbmap;
struct inode *ipbmap2;
struct inode *ipimap = sbi->ipimap;
struct jfs_log *log = sbi->log;
struct bmap *bmp = sbi->bmap;
s64 newLogAddress, newFSCKAddress;
int newFSCKSize;
s64 newMapSize = 0 , mapSize;
s64 XAddress, XSize, nblocks, xoff, xaddr, t64;
s64 oldLVSize;
s64 newFSSize;
s64 VolumeSize;
int newNpages = 0 , nPages, newPage, xlen, t32;
int tid;
int log_formatted = 0 ;
struct inode *iplist[1 ];
struct jfs_superblock *j_sb, *j_sb2;
s64 old_agsize;
int agsizechanged = 0 ;
struct buffer_head *bh, *bh2;
/* If the volume hasn't grown, get out now */
if (sbi->mntflag & JFS_INLINELOG)
oldLVSize = addressPXD(&sbi->logpxd) + lengthPXD(&sbi->logpxd);
else
oldLVSize = addressPXD(&sbi->fsckpxd) +
lengthPXD(&sbi->fsckpxd);
if (oldLVSize >= newLVSize) {
printk(KERN_WARNING
"jfs_extendfs: volume hasn't grown, returning\n" );
goto out;
}
VolumeSize = sb_bdev_nr_blocks(sb);
if (VolumeSize) {
if (newLVSize > VolumeSize) {
printk(KERN_WARNING "jfs_extendfs: invalid size\n" );
rc = -EINVAL;
goto out;
}
} else {
/* check the device */
bh = sb_bread(sb, newLVSize - 1 );
if (!bh) {
printk(KERN_WARNING "jfs_extendfs: invalid size\n" );
rc = -EINVAL;
goto out;
}
bforget(bh);
}
/* Can't extend write-protected drive */
if (isReadOnly(ipbmap)) {
printk(KERN_WARNING "jfs_extendfs: read-only file system\n" );
rc = -EROFS;
goto out;
}
/*
* reconfigure LV spaces
* - - - - - - - - - - - - - - - - - - - - -
*
* validate new size , or , if not specified , determine new size
*/
/*
* reconfigure inline log space :
*/
if ((sbi->mntflag & JFS_INLINELOG)) {
if (newLogSize == 0 ) {
/*
* no size specified : default to 1 / 256 of aggregate
* size ; rounded up to a megabyte boundary ;
*/
newLogSize = newLVSize >> 8 ;
t32 = (1 << (20 - sbi->l2bsize)) - 1 ;
newLogSize = (newLogSize + t32) & ~t32;
newLogSize =
min(newLogSize, MEGABYTE32 >> sbi->l2bsize);
} else {
/*
* convert the newLogSize to fs blocks .
*
* Since this is given in megabytes , it will always be
* an even number of pages .
*/
newLogSize = (newLogSize * MEGABYTE) >> sbi->l2bsize;
}
} else
newLogSize = 0 ;
newLogAddress = newLVSize - newLogSize;
/*
* reconfigure fsck work space :
*
* configure it to the end of the logical volume regardless of
* whether file system extends to the end of the aggregate ;
* Need enough 4 k pages to cover :
* - 1 bit per block in aggregate rounded up to BPERDMAP boundary
* - 1 extra page to handle control page and intermediate level pages
* - 50 extra pages for the chkdsk service log
*/
t64 = ((newLVSize - newLogSize + BPERDMAP - 1 ) >> L2BPERDMAP)
<< L2BPERDMAP;
t32 = DIV_ROUND_UP(t64, BITSPERPAGE) + 1 + 50 ;
newFSCKSize = t32 << sbi->l2nbperpage;
newFSCKAddress = newLogAddress - newFSCKSize;
/*
* compute new file system space ;
*/
newFSSize = newLVSize - newLogSize - newFSCKSize;
/* file system cannot be shrunk */
if (newFSSize < bmp->db_mapsize) {
rc = -EINVAL;
goto out;
}
/*
* If we ' re expanding enough that the inline log does not overlap
* the old one , we can format the new log before we quiesce the
* filesystem .
*/
if ((sbi->mntflag & JFS_INLINELOG) && (newLogAddress > oldLVSize)) {
if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
goto out;
log_formatted = 1 ;
}
/*
* quiesce file system
*
* ( prepare to move the inline log and to prevent map update )
*
* block any new transactions and wait for completion of
* all wip transactions and flush modified pages s . t .
* on - disk file system is in consistent state and
* log is not required for recovery .
*/
txQuiesce(sb);
/* Reset size of direct inode */
sbi->direct_inode->i_size = bdev_nr_bytes(sb->s_bdev);
if (sbi->mntflag & JFS_INLINELOG) {
/*
* deactivate old inline log
*/
lmLogShutdown(log);
/*
* mark on - disk super block for fs in transition ;
*
* update on - disk superblock for the new space configuration
* of inline log space and fsck work space descriptors :
* N . B . FS descriptor is NOT updated ;
*
* crash recovery :
* logredo ( ) : if FM_EXTENDFS , return to fsck ( ) for cleanup ;
* fsck ( ) : if FM_EXTENDFS , reformat inline log and fsck
* workspace from superblock inline log descriptor and fsck
* workspace descriptor ;
*/
/* read in superblock */
if ((rc = readSuper(sb, &bh)))
goto error_out;
j_sb = (struct jfs_superblock *)bh->b_data;
/* mark extendfs() in progress */
j_sb->s_state |= cpu_to_le32(FM_EXTENDFS);
j_sb->s_xsize = cpu_to_le64(newFSSize);
PXDaddress(&j_sb->s_xfsckpxd, newFSCKAddress);
PXDlength(&j_sb->s_xfsckpxd, newFSCKSize);
PXDaddress(&j_sb->s_xlogpxd, newLogAddress);
PXDlength(&j_sb->s_xlogpxd, newLogSize);
/* synchronously update superblock */
mark_buffer_dirty(bh);
sync_dirty_buffer(bh);
brelse(bh);
/*
* format new inline log synchronously ;
*
* crash recovery : if log move in progress ,
* reformat log and exit success ;
*/
if (!log_formatted)
if ((rc = lmLogFormat(log, newLogAddress, newLogSize)))
goto error_out;
/*
* activate new log
*/
log->base = newLogAddress;
log->size = newLogSize >> (L2LOGPSIZE - sb->s_blocksize_bits);
if ((rc = lmLogInit(log)))
goto error_out;
}
/*
* extend block allocation map
* - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* extendfs ( ) for new extension , retry after crash recovery ;
*
* note : both logredo ( ) and fsck ( ) rebuild map from
* the bitmap and configuration parameter from superblock
* ( disregarding all other control information in the map ) ;
*
* superblock :
* s_size : aggregate size in physical blocks ;
*/
/*
* compute the new block allocation map configuration
*
* map dinode :
* di_size : map file size in byte ;
* di_nblocks : number of blocks allocated for map file ;
* di_mapsize : number of blocks in aggregate ( covered by map ) ;
* map control page :
* db_mapsize : number of blocks in aggregate ( covered by map ) ;
*/
newMapSize = newFSSize;
/* number of data pages of new bmap file:
* roundup new size to full dmap page boundary and
* add 1 extra dmap page for next extendfs ( )
*/
t64 = (newMapSize - 1 ) + BPERDMAP;
newNpages = BLKTODMAPN(t64) + 1 ;
/*
* extend map from current map ( WITHOUT growing mapfile )
*
* map new extension with unmapped part of the last partial
* dmap page , if applicable , and extra page ( s ) allocated
* at end of bmap by mkfs ( ) or previous extendfs ( ) ;
*/
extendBmap:
/* compute number of blocks requested to extend */
mapSize = bmp->db_mapsize;
XAddress = mapSize; /* eXtension Address */
XSize = newMapSize - mapSize; /* eXtension Size */
old_agsize = bmp->db_agsize; /* We need to know if this changes */
/* compute number of blocks that can be extended by current mapfile */
t64 = dbMapFileSizeToMapSize(ipbmap);
if (mapSize > t64) {
printk(KERN_ERR "jfs_extendfs: mapSize (0x%Lx) > t64 (0x%Lx)\n" ,
(long long ) mapSize, (long long ) t64);
rc = -EIO;
goto error_out;
}
nblocks = min(t64 - mapSize, XSize);
/*
* update map pages for new extension :
*
* update / init dmap and bubble up the control hierarchy
* incrementally fold up dmaps into upper levels ;
* update bmap control page ;
*/
if ((rc = dbExtendFS(ipbmap, XAddress, nblocks)))
goto error_out;
agsizechanged |= (bmp->db_agsize != old_agsize);
/*
* the map now has extended to cover additional nblocks :
* dn_mapsize = oldMapsize + nblocks ;
*/
/* ipbmap->i_mapsize += nblocks; */
XSize -= nblocks;
/*
* grow map file to cover remaining extension
* and / or one extra dmap page for next extendfs ( ) ;
*
* allocate new map pages and its backing blocks , and
* update map file xtree
*/
/* compute number of data pages of current bmap file */
nPages = ipbmap->i_size >> L2PSIZE;
/* need to grow map file ? */
if (nPages == newNpages)
goto finalizeBmap;
/*
* grow bmap file for the new map pages required :
*
* allocate growth at the start of newly extended region ;
* bmap file only grows sequentially , i . e . , both data pages
* and possibly xtree index pages may grow in append mode ,
* s . t . logredo ( ) can reconstruct pre - extension state
* by washing away bmap file of pages outside s_size boundary ;
*/
/*
* journal map file growth as if a regular file growth :
* ( note : bmap is created with di_mode = IFJOURNAL | IFREG ) ;
*
* journaling of bmap file growth is not required since
* logredo ( ) do / can not use log records of bmap file growth
* but it provides careful write semantics , pmap update , etc . ;
*/
/* synchronous write of data pages: bmap data pages are
* cached in meta - data cache , and not written out
* by txCommit ( ) ;
*/
rc = filemap_fdatawait(ipbmap->i_mapping);
if (rc)
goto error_out;
rc = filemap_write_and_wait(ipbmap->i_mapping);
if (rc)
goto error_out;
diWriteSpecial(ipbmap, 0 );
newPage = nPages; /* first new page number */
xoff = newPage << sbi->l2nbperpage;
xlen = (newNpages - nPages) << sbi->l2nbperpage;
xlen = min(xlen, (int ) nblocks) & ~(sbi->nbperpage - 1 );
xaddr = XAddress;
tid = txBegin(sb, COMMIT_FORCE);
if ((rc = xtAppend(tid, ipbmap, 0 , xoff, nblocks, &xlen, &xaddr, 0 ))) {
txEnd(tid);
goto error_out;
}
/* update bmap file size */
ipbmap->i_size += xlen << sbi->l2bsize;
inode_add_bytes(ipbmap, xlen << sbi->l2bsize);
iplist[0 ] = ipbmap;
rc = txCommit(tid, 1 , &iplist[0 ], COMMIT_FORCE);
txEnd(tid);
if (rc)
goto error_out;
/*
* map file has been grown now to cover extension to further out ;
* di_size = new map file size ;
*
* if huge extension , the previous extension based on previous
* map file size may not have been sufficient to cover whole extension
* ( it could have been used up for new map pages ) ,
* but the newly grown map file now covers lot bigger new free space
* available for further extension of map ;
*/
/* any more blocks to extend ? */
if (XSize)
goto extendBmap;
finalizeBmap:
/* finalize bmap */
dbFinalizeBmap(ipbmap);
/*
* update inode allocation map
* - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* move iag lists from old to new iag ;
* agstart field is not updated for logredo ( ) to reconstruct
* iag lists if system crash occurs .
* ( computation of ag number from agstart based on agsize
* will correctly identify the new ag ) ;
*/
/* if new AG size the same as old AG size, done! */
if (agsizechanged) {
if ((rc = diExtendFS(ipimap, ipbmap)))
goto error_out;
/* finalize imap */
if ((rc = diSync(ipimap)))
goto error_out;
}
/*
* finalize
* - - - - - - - -
*
* extension is committed when on - disk super block is
* updated with new descriptors : logredo will recover
* crash before it to pre - extension state ;
*/
/* sync log to skip log replay of bmap file growth transaction; */
/* lmLogSync(log, 1); */
/*
* synchronous write bmap global control page ;
* for crash before completion of write
* logredo ( ) will recover to pre - extendfs state ;
* for crash after completion of write ,
* logredo ( ) will recover post - extendfs state ;
*/
if ((rc = dbSync(ipbmap)))
goto error_out;
/*
* copy primary bmap inode to secondary bmap inode
*/
ipbmap2 = diReadSpecial(sb, BMAP_I, 1 );
if (ipbmap2 == NULL) {
printk(KERN_ERR "jfs_extendfs: diReadSpecial(bmap) failed\n" );
goto error_out;
}
memcpy(&JFS_IP(ipbmap2)->i_xtroot, &JFS_IP(ipbmap)->i_xtroot, 288 );
ipbmap2->i_size = ipbmap->i_size;
ipbmap2->i_blocks = ipbmap->i_blocks;
diWriteSpecial(ipbmap2, 1 );
diFreeSpecial(ipbmap2);
/*
* update superblock
*/
if ((rc = readSuper(sb, &bh)))
goto error_out;
j_sb = (struct jfs_superblock *)bh->b_data;
/* mark extendfs() completion */
j_sb->s_state &= cpu_to_le32(~FM_EXTENDFS);
j_sb->s_size = cpu_to_le64(bmp->db_mapsize <<
le16_to_cpu(j_sb->s_l2bfactor));
j_sb->s_agsize = cpu_to_le32(bmp->db_agsize);
/* update inline log space descriptor */
if (sbi->mntflag & JFS_INLINELOG) {
PXDaddress(&(j_sb->s_logpxd), newLogAddress);
PXDlength(&(j_sb->s_logpxd), newLogSize);
}
/* record log's mount serial number */
j_sb->s_logserial = cpu_to_le32(log->serial);
/* update fsck work space descriptor */
PXDaddress(&(j_sb->s_fsckpxd), newFSCKAddress);
PXDlength(&(j_sb->s_fsckpxd), newFSCKSize);
j_sb->s_fscklog = 1 ;
/* sb->s_fsckloglen remains the same */
/* Update secondary superblock */
bh2 = sb_bread(sb, SUPER2_OFF >> sb->s_blocksize_bits);
if (bh2) {
j_sb2 = (struct jfs_superblock *)bh2->b_data;
memcpy(j_sb2, j_sb, sizeof (struct jfs_superblock));
mark_buffer_dirty(bh);
sync_dirty_buffer(bh2);
brelse(bh2);
}
/* write primary superblock */
mark_buffer_dirty(bh);
sync_dirty_buffer(bh);
brelse(bh);
goto resume;
error_out:
jfs_error(sb, "\n" );
resume:
/*
* resume file system transactions
*/
txResume(sb);
out:
return rc;
}
Messung V0.5 in Prozent C=89 H=48 G=71
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland