/*
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*
* 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.
*
* This program is distributed in the hope that it would 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 the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_types.h"
#include "xfs_bit.h"
#include "xfs_log.h"
#include "xfs_inum.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_dir2.h"
#include "xfs_dmapi.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_alloc_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_dir2_sf.h"
#include "xfs_attr_sf.h"
#include "xfs_dinode.h"
#include "xfs_inode.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_alloc.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_bmap.h"
/*
* Allocation group level functions.
*/
static inline int
xfs_ialloc_cluster_alignment(
xfs_alloc_arg_t *args)
{
if (xfs_sb_version_hasalign(&args->mp->m_sb) &&
args->mp->m_sb.sb_inoalignmt >=
XFS_B_TO_FSBT(args->mp, XFS_INODE_CLUSTER_SIZE(args->mp)))
return args->mp->m_sb.sb_inoalignmt;
return 1;
}
/*
* Lookup a record by ino in the btree given by cur.
*/
int /* error */
xfs_inobt_lookup(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_agino_t ino, /* starting inode of chunk */
xfs_lookup_t dir, /* <=, >=, == */
int *stat) /* success/failure */
{
cur->bc_rec.i.ir_startino = ino;
cur->bc_rec.i.ir_freecount = 0;
cur->bc_rec.i.ir_free = 0;
return xfs_btree_lookup(cur, dir, stat);
}
/*
* Update the record referred to by cur to the value given.
* This either works (return 0) or gets an EFSCORRUPTED error.
*/
STATIC int /* error */
xfs_inobt_update(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_inobt_rec_incore_t *irec) /* btree record */
{
union xfs_btree_rec rec;
rec.inobt.ir_startino = cpu_to_be32(irec->ir_startino);
rec.inobt.ir_freecount = cpu_to_be32(irec->ir_freecount);
rec.inobt.ir_free = cpu_to_be64(irec->ir_free);
return xfs_btree_update(cur, &rec);
}
/*
* Get the data from the pointed-to record.
*/
int /* error */
xfs_inobt_get_rec(
struct xfs_btree_cur *cur, /* btree cursor */
xfs_inobt_rec_incore_t *irec, /* btree record */
int *stat) /* output: success/failure */
{
union xfs_btree_rec *rec;
int error;
error = xfs_btree_get_rec(cur, &rec, stat);
if (!error && *stat == 1) {
irec->ir_startino = be32_to_cpu(rec->inobt.ir_startino);
irec->ir_freecount = be32_to_cpu(rec->inobt.ir_freecount);
irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
}
return error;
}
/*
* Verify that the number of free inodes in the AGI is correct.
*/
#ifdef DEBUG
STATIC int
xfs_check_agi_freecount(
struct xfs_btree_cur *cur,
struct xfs_agi *agi)
{
if (cur->bc_nlevels == 1) {
xfs_inobt_rec_incore_t rec;
int freecount = 0;
int error;
int i;
error = xfs_inobt_lookup(cur, 0, XFS_LOOKUP_GE, &i);
if (error)
return error;
do {
error = xfs_inobt_get_rec(cur, &rec, &i);
if (error)
return error;
if (i) {
freecount += rec.ir_freecount;
error = xfs_btree_increment(cur, 0, &i);
if (error)
return error;
}
} while (i == 1);
if (!XFS_FORCED_SHUTDOWN(cur->bc_mp))
ASSERT(freecount == be32_to_cpu(agi->agi_freecount));
}
return 0;
}
#else
#define xfs_check_agi_freecount(cur, agi) 0
#endif
/*
* Initialise a new set of inodes.
*/
STATIC void
xfs_ialloc_inode_init(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_agnumber_t agno,
xfs_agblock_t agbno,
xfs_agblock_t length,
unsigned int gen)
{
struct xfs_buf *fbuf;
struct xfs_dinode *free;
int blks_per_cluster, nbufs, ninodes;
int version;
int i, j;
xfs_daddr_t d;
/*
* Loop over the new block(s), filling in the inodes.
* For small block sizes, manipulate the inodes in buffers
* which are multiples of the blocks size.
*/
if (mp->m_sb.sb_blocksize >= XFS_INODE_CLUSTER_SIZE(mp)) {
blks_per_cluster = 1;
nbufs = length;
ninodes = mp->m_sb.sb_inopblock;
} else {
blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) /
mp->m_sb.sb_blocksize;
nbufs = length / blks_per_cluster;
ninodes = blks_per_cluster * mp->m_sb.sb_inopblock;
}
/*
* Figure out what version number to use in the inodes we create.
* If the superblock version has caught up to the one that supports
* the new inode format, then use the new inode version. Otherwise
* use the old version so that old kernels will continue to be
* able to use the file system.
*/
if (xfs_sb_version_hasnlink(&mp->m_sb))
version = 2;
else
version = 1;
for (j = 0; j < nbufs; j++) {
/*
* Get the block.
*/
d = XFS_AGB_TO_DADDR(mp, agno, agbno + (j * blks_per_cluster));
fbuf = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
mp->m_bsize * blks_per_cluster,
XFS_BUF_LOCK);
ASSERT(fbuf);
ASSERT(!XFS_BUF_GETERROR(fbuf));
/*
* Initialize all inodes in this buffer and then log them.
*
* XXX: It would be much better if we had just one transaction
* to log a whole cluster of inodes instead of all the
* individual transactions causing a lot of log traffic.
*/
xfs_biozero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog);
for (i = 0; i < ninodes; i++) {
int ioffset = i << mp->m_sb.sb_inodelog;
uint isize = sizeof(struct xfs_dinode);
free = xfs_make_iptr(mp, fbuf, i);
free->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
free->di_version = version;
free->di_gen = cpu_to_be32(gen);
free->di_next_unlinked = cpu_to_be32(NULLAGINO);
xfs_trans_log_buf(tp, fbuf, ioffset, ioffset + isize - 1);
}
xfs_trans_inode_alloc_buf(tp, fbuf);
}
}
/*
* Allocate new inodes in the allocation group specified by agbp.
* Return 0 for success, else error code.
*/
STATIC int /* error code or 0 */
xfs_ialloc_ag_alloc(
xfs_trans_t *tp, /* transaction pointer */
xfs_buf_t *agbp, /* alloc grou
|