File: [Development] / linux-2.6-xfs / fs / xfs / xfs_mount.c (download)
Revision 1.69, Tue Jul 19 21:58:45 1994 UTC (23 years, 3 months ago) by doucette
Branch: MAIN
Changes since 1.68: +0 -1
lines
Remove assert on xfs_mountfs returning an error, from SIM only.
Make callers figure it out.
|
#ident "$Revision: 1.67 $"
#include <sys/param.h>
#ifdef SIM
#define _KERNEL
#endif
#include <sys/buf.h>
#include <sys/sysmacros.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/grio.h>
#ifdef SIM
#undef _KERNEL
#endif
#include <sys/uuid.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/open.h>
#include <sys/cred.h>
#ifdef SIM
#include <bstring.h>
#else
#include <sys/systm.h>
#include <sys/conf.h>
#endif
#include <stddef.h>
#include "xfs_types.h"
#include "xfs_inum.h"
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_ag.h"
#include "xfs_mount.h"
#include "xfs_alloc_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_ialloc_btree.h"
#include "xfs_btree.h"
#include "xfs_ialloc.h"
#include "xfs_dinode.h"
#include "xfs_inode_item.h"
#include "xfs_inode.h"
#include "xfs_dir.h"
#include "xfs_alloc.h"
#include "xfs_rtalloc.h"
#include "xfs_bmap.h"
#include "xfs_error.h"
#include "xfs_bit.h"
#ifdef SIM
#include "sim.h"
#endif
STATIC int xfs_mod_incore_sb_unlocked(xfs_mount_t *, uint, int);
STATIC void xfs_sb_relse(buf_t *);
/*
* Return a pointer to an initialized xfs_mount structure.
*/
xfs_mount_t *
xfs_mount_init(void)
{
xfs_mount_t *mp;
mp = kmem_zalloc(sizeof(*mp), 0);
initnlock(&mp->m_ail_lock, "xfs_ail");
initnlock(&mp->m_async_lock, "xfs_async");
initnsema(&mp->m_ilock, 1, "xfs_ilock");
initnlock(&mp->m_ipinlock, "xfs_ipin");
initnlock(&mp->m_sb_lock, "xfs_sb");
/*
* Initialize the AIL.
*/
xfs_trans_ail_init(mp);
return mp;
}
/*
* xfs_mountfs XXXjleong Needs more error checking
*
* This function does the following on an initial mount of a file system:
* - reads the superblock from disk and init the mount struct
* - init mount struct realtime fields
* - allocate inode hash table for fs
* - init directory manager
* Note:
* - does not handle remounts
* - does not start the log manager
*/
int
xfs_mountfs(vfs_t *vfsp, dev_t dev)
{
buf_t *bp;
xfs_sb_t *sbp;
int error = 0;
int s, i, brsize;
xfs_mount_t *mp;
xfs_inode_t *rip;
vnode_t *rvp = 0;
if (vfsp->vfs_flag & VFS_REMOUNT)
return 0;
mp = XFS_VFSTOM(vfsp);
ASSERT(mp->m_sb_bp == 0);
/*
* Allocate a (locked) buffer to hold the superblock.
* This will be kept around at all time to optimize
* access to the superblock.
*/
bp = ngetrbuf(BBTOB(BTOBB(sizeof(xfs_sb_t))));
ASSERT(bp != NULL);
ASSERT((bp->b_flags & B_BUSY) && valusema(&bp->b_lock) <= 0);
/*
* Initialize and read in the superblock buffer.
*/
bp->b_edev = dev;
bp->b_relse = xfs_sb_relse;
bp->b_blkno = XFS_SB_DADDR;
bp->b_flags |= B_READ;
bdstrat(bmajor(dev), bp);
if (error = iowait(bp)) {
ASSERT(error == 0);
goto bad;
}
/*
* Initialize the mount structure from the superblock.
*/
sbp = XFS_BUF_TO_SBP(bp);
if ((sbp->sb_magicnum != XFS_SB_MAGIC) ||
(sbp->sb_versionnum != XFS_SB_VERSION)) {
error = XFS_ERROR(EINVAL);
goto bad;
}
mp->m_sb_bp = bp;
mp->m_sb = *sbp; /* bcopy structure */
brelse(bp);
ASSERT(valusema(&bp->b_lock) > 0);
sbp = &(mp->m_sb);
mp->m_agfrotor = mp->m_agirotor = 0;
mp->m_blkbit_log = sbp->sb_blocklog + XFS_NBBYLOG;
mp->m_blkbb_log = sbp->sb_blocklog - BBSHIFT;
mp->m_agno_log = xfs_highbit32(sbp->sb_agcount - 1) + 1;
mp->m_blockmask = sbp->sb_blocksize - 1;
mp->m_blockwsize = sbp->sb_blocksize >> XFS_WORDLOG;
mp->m_blockwmask = mp->m_blockwsize - 1;
for (i = 0; i < 2; i++) {
mp->m_alloc_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
xfs_alloc, i == 0);
mp->m_alloc_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
xfs_alloc, i == 0);
}
for (i = 0; i < 2; i++) {
mp->m_bmap_dmxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
xfs_bmbt, i == 0);
mp->m_bmap_dmnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
xfs_bmbt, i == 0);
}
brsize = XFS_BMAP_BROOT_SIZE(sbp->sb_inodesize);
for (i = 0; i < 2; i++) {
mp->m_bmap_dmxr[i + 2] = XFS_BTREE_BLOCK_MAXRECS(brsize,
xfs_bmdr, i == 0);
ASSERT(mp->m_bmap_dmxr[i + 2] >= 2);
mp->m_bmap_dmnr[i + 2] = XFS_BTREE_BLOCK_MINRECS(brsize,
xfs_bmdr, i == 0);
}
mp->m_bmap_ext_mxr = brsize / sizeof(xfs_bmbt_rec_t);
for (i = 0; i < 2; i++) {
mp->m_inobt_mxr[i] = XFS_BTREE_BLOCK_MAXRECS(sbp->sb_blocksize,
xfs_inobt, i == 0);
mp->m_inobt_mnr[i] = XFS_BTREE_BLOCK_MINRECS(sbp->sb_blocksize,
xfs_inobt, i == 0);
}
xfs_alloc_compute_maxlevels(mp);
xfs_bmap_compute_maxlevels(mp);
xfs_ialloc_compute_maxlevels(mp);
mp->m_bsize = XFS_FSB_TO_BB(mp, 1);
vfsp->vfs_bsize = XFS_FSB_TO_B(mp, 1);
/*
* Set the default minimum read and write sizes.
*/
if (sbp->sb_blocklog > XFS_READIO_LOG) {
mp->m_readio_log = sbp->sb_blocklog;
} else {
mp->m_readio_log = XFS_READIO_LOG;
}
mp->m_readio_blocks = 1 << (mp->m_readio_log - sbp->sb_blocklog);
if (sbp->sb_blocklog > XFS_WRITEIO_LOG) {
mp->m_writeio_log = sbp->sb_blocklog;
} else {
mp->m_writeio_log = XFS_WRITEIO_LOG;
}
mp->m_writeio_blocks = 1 << (mp->m_writeio_log - sbp->sb_blocklog);
/*
* Initialize realtime fields in the mount structure
*/
if (sbp->sb_rblocks) {
mp->m_rsumlevels = sbp->sb_rextslog + 1;
mp->m_rsumsize = sizeof(xfs_suminfo_t) * mp->m_rsumlevels * sbp->sb_rbmblocks;
mp->m_rsumsize = roundup(mp->m_rsumsize, sbp->sb_blocksize);
mp->m_rbmip = mp->m_rsumip = NULL;
}
/*
* Allocate and initialize the inode hash table for this
* file system.
*/
xfs_ihash_init(mp);
/*
* Allocate and initialize the per-ag data.
*/
mp->m_perag = kmem_zalloc(sbp->sb_agcount * sizeof(xfs_perag_t),
KM_SLEEP);
/*
* Call the log's mount-time initialization.
* xfs_log_mount() always does XFS_LOG_RECOVER.
*/
if (sbp->sb_logblocks > 0) { /* check for volume case */
error = xfs_log_mount(mp, mp->m_logdev,
XFS_FSB_TO_DADDR(mp, sbp->sb_logstart),
XFS_FSB_TO_BB(mp, sbp->sb_logblocks));
if (error > 0) {
/*
* XXX log recovery failure - What action should be
* taken? Translate log error to something user
* understandable
*/
error = XFS_ERROR(EBUSY);
}
} else {
/*
* No log has been defined.
*/
error = XFS_ERROR(EINVAL);
}
if (error) {
/*
* XXX memory that has been allocated for the
* mount structure needs to be freed.
*/
return(error);
}
#ifdef SIM
/*
* Mkfs calls mount before the root inode is allocated.
*/
if (sbp->sb_rootino != NULLFSINO)
#endif
{
/*
* Get and sanity-check the root inode.
* Save the pointer to it in the mount structure.
*/
rip = xfs_iget(mp, NULL, sbp->sb_rootino, XFS_ILOCK_EXCL);
rvp = XFS_ITOV(rip);
if ((rip->i_d.di_mode & IFMT) != IFDIR) {
vmap_t vmap;
VMAP(rvp, vmap);
VN_RELE(rvp);
vn_purge(rvp, &vmap);
prdev("Root inode %d is not a directory",
rip->i_dev, rip->i_ino);
error = XFS_ERROR(EINVAL);
xfs_iunlock(rip, XFS_ILOCK_EXCL);
return (error);
}
s = VN_LOCK(rvp);
rvp->v_flag |= VROOT;
VN_UNLOCK(rvp, s);
mp->m_rootip = rip; /* save it */
xfs_iunlock(rip, XFS_ILOCK_EXCL);
}
/*
* Initialize realtime inode pointers in the mount structure
*/
if (sbp->sb_rbmino != NULLFSINO) {
mp->m_rbmip = xfs_iget(mp, NULL, sbp->sb_rbmino, NULL);
ASSERT(sbp->sb_rsumino != NULLFSINO);
mp->m_rsumip = xfs_iget(mp, NULL, sbp->sb_rsumino, NULL);
}
/*
* Initialize directory manager's entries.
*/
xfs_dir_mount(mp);
return 0;
bad:
brelse(bp);
freerbuf(bp);
return error;
}
#ifdef SIM
/*
* xfs_mount is the function used by the simulation environment
* to start the file system.
*/
xfs_mount_t *
xfs_mount(dev_t dev, dev_t logdev, dev_t rtdev)
{
int error;
xfs_mount_t *mp;
vfs_t *vfsp;
extern vfsops_t xfs_vfsops;
mp = xfs_mount_init();
vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP);
VFS_INIT(vfsp, &xfs_vfsops, NULL);
mp->m_vfsp = vfsp;
vfsp->vfs_data = mp;
mp->m_dev = dev;
mp->m_rtdev = rtdev;
vfsp->vfs_dev = dev;
error = xfs_mountfs(vfsp, dev);
if (error) {
kmem_free(mp, sizeof(*mp));
return 0;
}
/*
* Call the log's mount-time initialization.
*/
if (logdev) {
xfs_sb_t *sbp;
xfs_fsblock_t logstart;
sbp = XFS_BUF_TO_SBP(mp->m_sb_bp);
logstart = sbp->sb_logstart;
xfs_log_mount(mp, logdev, XFS_FSB_TO_DADDR(mp, logstart),
XFS_FSB_TO_BB(mp, sbp->sb_logblocks));
}
return mp;
}
#endif
/*
* xfs_unmountfs
*/
int
xfs_unmountfs(xfs_mount_t *mp, int vfs_flags, struct cred *cr)
{
buf_t *bp;
dev_t dev;
int error;
xfs_iflush_all(mp, 0);
bflush(mp->m_dev);
if (mp->m_rtdev)
bflush(mp->m_rtdev);
bp = xfs_getsb(mp, 0);
bp->b_flags &= ~(B_DONE | B_READ);
bp->b_flags |= B_WRITE;
bwait_unpin(bp);
dev = mp->m_dev;
bdstrat(bmajor(mp->m_dev), bp);
error = iowait(bp);
ASSERT(error == 0);
xfs_log_unmount(mp); /* Done! No more fs ops. */
if (mp->m_ddevp) {
VOP_CLOSE(mp->m_ddevp, vfs_flags, 1, 0, cr);
VN_RELE(mp->m_ddevp);
}
if (mp->m_rtdevp) {
VOP_CLOSE(mp->m_rtdevp, vfs_flags, 1, 0, cr);
VN_RELE(mp->m_rtdevp);
}
if (mp->m_logdevp && mp->m_logdevp != mp->m_ddevp) {
VOP_CLOSE(mp->m_logdevp, vfs_flags, 1, 0, cr);
VN_RELE(mp->m_logdevp);
}
nfreerbuf(bp);
xfs_ihash_free(mp);
kmem_free(mp->m_perag, sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
freesplock(mp->m_ail_lock);
freesplock(mp->m_async_lock);
freesema(&mp->m_ilock);
freesplock(mp->m_ipinlock);
freesplock(mp->m_sb_lock);
kmem_free(mp, sizeof(*mp));
}
#ifdef SIM
/*
* xfs_umount is the function used by the simulation environment
* to stop the file system.
*/
void
xfs_umount(xfs_mount_t *mp)
{
xfs_unmountfs(mp, 0, NULL);
}
#endif
/*
* xfs_mod_sb() can be used to copy arbitrary changes to the
* in-core superblock into the superblock buffer to be logged.
* It does not provide the higher level of locking that is
* needed to protect the in-core superblock from concurrent
* access.
*/
void
xfs_mod_sb(xfs_trans_t *tp, int fields)
{
buf_t *bp;
int first;
int last;
xfs_mount_t *mp;
xfs_sb_t *sbp;
static const int offsets[] = {
offsetof(xfs_sb_t, sb_magicnum),
offsetof(xfs_sb_t, sb_blocksize),
offsetof(xfs_sb_t, sb_dblocks),
offsetof(xfs_sb_t, sb_rblocks),
offsetof(xfs_sb_t, sb_rextents),
offsetof(xfs_sb_t, sb_uuid),
offsetof(xfs_sb_t, sb_logstart),
offsetof(xfs_sb_t, sb_rootino),
offsetof(xfs_sb_t, sb_rbmino),
offsetof(xfs_sb_t, sb_rsumino),
offsetof(xfs_sb_t, sb_rextsize),
offsetof(xfs_sb_t, sb_agblocks),
offsetof(xfs_sb_t, sb_agcount),
offsetof(xfs_sb_t, sb_rbmblocks),
offsetof(xfs_sb_t, sb_logblocks),
offsetof(xfs_sb_t, sb_versionnum),
offsetof(xfs_sb_t, sb_sectsize),
offsetof(xfs_sb_t, sb_inodesize),
offsetof(xfs_sb_t, sb_inopblock),
offsetof(xfs_sb_t, sb_fname[0]),
offsetof(xfs_sb_t, sb_fpack[0]),
offsetof(xfs_sb_t, sb_blocklog),
offsetof(xfs_sb_t, sb_sectlog),
offsetof(xfs_sb_t, sb_inodelog),
offsetof(xfs_sb_t, sb_inopblog),
offsetof(xfs_sb_t, sb_agblklog),
offsetof(xfs_sb_t, sb_rextslog),
offsetof(xfs_sb_t, sb_icount),
offsetof(xfs_sb_t, sb_ifree),
offsetof(xfs_sb_t, sb_fdblocks),
offsetof(xfs_sb_t, sb_frextents),
sizeof(xfs_sb_t)
};
mp = tp->t_mountp;
bp = xfs_trans_getsb(tp, 0);
sbp = XFS_BUF_TO_SBP(bp);
xfs_btree_offsets(fields, offsets, XFS_SB_NUM_BITS, &first, &last);
bcopy((caddr_t)&mp->m_sb + first, (caddr_t)sbp + first, last - first + 1);
xfs_trans_log_buf(tp, bp, first, last);
}
/*
* xfs_mod_incore_sb_unlocked() is a utility routine common used to apply
* a delta to a specified field in the in-core superblock. Simply
* switch on the field indicated and apply the delta to that field.
* Fields are not allowed to dip below zero, so if the delta would
* do this do not apply it and return EINVAL.
*
* The SB_LOCK must be held when this routine is called.
*/
STATIC int
xfs_mod_incore_sb_unlocked(xfs_mount_t *mp, uint field, int delta)
{
register int scounter; /* short counter for 32 bit fields */
register long long lcounter; /* long counter for 64 bit fields */
/*
* Obtain the in-core superblock spin lock and switch
* on the indicated field. Apply the delta to the
* proper field. If the fields value would dip below
* 0, then do not apply the delta and return EINVAL.
*/
switch (field) {
case XFS_SB_ICOUNT:
lcounter = mp->m_sb.sb_icount;
lcounter += delta;
if (lcounter < 0) {
ASSERT(0);
return (XFS_ERROR(EINVAL));
}
mp->m_sb.sb_icount = lcounter;
return (0);
case XFS_SB_IFREE:
lcounter = mp->m_sb.sb_ifree;
lcounter += delta;
if (lcounter < 0) {
ASSERT(0);
return (XFS_ERROR(EINVAL));
}
mp->m_sb.sb_ifree = lcounter;
return (0);
case XFS_SB_FDBLOCKS:
lcounter = mp->m_sb.sb_fdblocks;
lcounter += delta;
if (lcounter < 0) {
return (XFS_ERROR(ENOSPC));
}
mp->m_sb.sb_fdblocks = lcounter;
return (0);
case XFS_SB_FREXTENTS:
lcounter = mp->m_sb.sb_frextents;
lcounter += delta;
if (lcounter < 0) {
return (XFS_ERROR(ENOSPC));
}
mp->m_sb.sb_frextents = lcounter;
return (0);
default:
ASSERT(0);
return (XFS_ERROR(EINVAL));
}
}
/*
* xfs_mod_incore_sb() is used to change a field in the in-core
* superblock structure by the specified delta. This modification
* is protected by the SB_LOCK. Just use the xfs_mod_incore_sb_unlocked()
* routine to do the work.
*/
int
xfs_mod_incore_sb(xfs_mount_t *mp, uint field, int delta)
{
int s;
int status;
s = XFS_SB_LOCK(mp);
status = xfs_mod_incore_sb_unlocked(mp, field, delta);
XFS_SB_UNLOCK(mp, s);
return (status);
}
/*
* xfs_mod_incore_sb_batch() is used to change more than one field
* in the in-core superblock structure at a time. This modification
* is protected by a lock internal to this module. The fields and
* changes to those fields are specified in the array of xfs_mod_sb
* structures passed in.
*
* Either all of the specified deltas will be applied or none of
* them will. If any modified field dips below 0, then all modifications
* will be backed out and EINVAL will be returned.
*/
int
xfs_mod_incore_sb_batch(xfs_mount_t *mp, xfs_mod_sb_t *msb, uint nmsb)
{
int s;
int n;
int status;
xfs_mod_sb_t *msbp;
/*
* Loop through the array of mod structures and apply each
* individually. If any fail, then back out all those
* which have already been applied. Do all of this within
* the scope of the SB_LOCK so that all of the changes will
* be atomic.
*/
s = XFS_SB_LOCK(mp);
msbp = &msb[0];
for (msbp = &msbp[0]; msbp < (msb + nmsb); msbp++) {
/*
* Apply the delta at index n. If it fails, break
* from the loop so we'll fall into the undo loop
* below.
*/
status = xfs_mod_incore_sb_unlocked(mp, msbp->msb_field,
msbp->msb_delta);
if (status != 0) {
break;
}
}
/*
* If we didn't complete the loop above, then back out
* any changes made to the superblock. If you add code
* between the loop above and here, make sure that you
* preserve the value of status. Loop back until
* we step below the beginning of the array. Make sure
* we don't touch anything back there.
*/
if (status != 0) {
msbp--;
while (msbp >= msb) {
status = xfs_mod_incore_sb_unlocked(mp,
msbp->msb_field, -(msbp->msb_delta));
ASSERT(status == 0);
msbp--;
}
}
XFS_SB_UNLOCK(mp, s);
return (status);
}
/*
* xfs_getsb() is called to obtain the buffer for the superblock.
* The buffer is returned locked and read in from disk.
* The buffer should be released with a call to xfs_brelse().
*
* If the flags parameter is BUF_TRYLOCK, then we'll only return
* the superblock buffer if it can be locked without sleeping.
* If it can't then we'll return NULL.
*/
buf_t *
xfs_getsb(xfs_mount_t *mp,
int flags)
{
buf_t *bp;
ASSERT(mp->m_sb_bp != NULL);
bp = mp->m_sb_bp;
if (flags & BUF_TRYLOCK) {
if (!cpsema(&bp->b_lock)) {
return NULL;
}
} else {
psema(&bp->b_lock, PRIBIO);
}
ASSERT(bp->b_flags & B_DONE);
return (bp);
}
/*
* This is the brelse function for the private superblock buffer.
* All it needs to do is unlock the buffer and clear any spurious
* flags.
*/
STATIC void
xfs_sb_relse(buf_t *bp)
{
ASSERT(bp->b_flags & B_BUSY);
ASSERT(valusema(&bp->b_lock) <= 0);
bp->b_flags &= ~(B_ASYNC | B_READ);
bp->b_flags2 = 0;
bp->av_forw = NULL;
bp->av_back = NULL;
vsema(&bp->b_lock);
}