[BACK]Return to xfs_vfs.c CVS log [TXT][DIR] Up to [Development] / xfs-linux / linux-2.6

File: [Development] / xfs-linux / linux-2.6 / Attic / xfs_vfs.c (download)

Revision 1.35, Tue Jun 11 18:54:30 2002 UTC (15 years, 4 months ago) by lord
Branch: MAIN
Changes since 1.34: +1 -8 lines

more minor cleanup
Merge of 2.4.x-xfs:slinx:121226a by lord.

/*
 * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 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.
 * 
 * Further, this software is distributed without any warranty that it is
 * free of the rightful claim of any third person regarding infringement
 * or the like.  Any license provided herein, whether implied or
 * otherwise, applies only to this software file.  Patent licenses, if
 * any, provided herein do not apply to combinations of this program with
 * other software, or any other product whatsoever.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
 * 
 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
 * Mountain View, CA  94043, or:
 * 
 * http://www.sgi.com 
 * 
 * For further information regarding this notice, see: 
 * 
 * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
 */

#include <xfs.h>
#include <linux/smp_lock.h>


/*
 * VFS global data.
 */
STATIC spinlock_t	vfslock;	/* spinlock protecting rootvfs and vfs_flag */

#define		vfsp_wait(V,P,S)        sv_wait(&(V)->vfs_wait,P,&vfslock,S)
#define		vfsp_waitsig(V,P,S)     sv_wait_sig(&(V)->vfs_wait,P,&vfslock,S)

/*
 * Allocate and initialize a new vfs
 */
vfs_t *
vfs_allocate(void)
{
	vfs_t	*vfsp;

	vfsp = kmalloc(sizeof(vfs_t), GFP_KERNEL);
	if (vfsp) {
		memset(vfsp, 0, sizeof(vfs_t));
		VFS_INIT(vfsp);
	}
	return (vfsp);
}

void
vfs_deallocate(vfs_t *vfsp)
{
        VFS_FREE(vfsp);
        kfree(vfsp);
}

/*
 * Implement a simplified multi-access, single-update protocol for vfs.
 *
 * Only one update-lock (mount/unmount) can be held at a time; if another
 * process holds the vfs stucture with update capabilities, or is waiting
 * to acquire update rights, other update calls fail.
 *
 * Multiple accessors are allowed: vfs_busycnt tracks the number of
 * concurrent accesses.  Update permission sleeps until the last access
 * has finished, but leaves the VFS_MWANT flag to hold (if called via
 * vfs_busy) or reject (called via vfs_busydev or vfs_lock) subsequent
 * accesses/updates.
 * Note that traverse understands the vfs locking model and waits for
 * any update to complete, and retries the mount-point traversal.
 *
 * Accessors include: vfs_syncall, traverse, VFS_STATVFS, and quota checking.
 */
STATIC int
vfs_lock_flags(struct vfs *vfsp, int flags)
{
	register int error;

	spin_lock(&vfslock);
	if (vfsp->vfs_flag & (VFS_MLOCK|VFS_MWANT)) {
		spin_unlock(&vfslock);
		return EBUSY;
	}

	while (vfsp->vfs_busycnt) {
		ASSERT(vfsp->vfs_busycnt > 0);
		ASSERT(!(vfsp->vfs_flag & (VFS_MLOCK|VFS_OFFLINE)));
		vfsp->vfs_flag |= VFS_MWAIT|VFS_MWANT;
		vfsp_waitsig(vfsp, PVFS, 0); /* REMOVED setting error. */
		error = 0; /* JIMJIM always no error */
		spin_lock(&vfslock);
		if (error) {
			ASSERT(vfsp->vfs_flag & VFS_MWANT);
			vfsp->vfs_flag &= ~VFS_MWANT;
			if (vfsp->vfs_flag & VFS_MWAIT) {
				vfsp->vfs_flag &= ~VFS_MWAIT;
				sv_broadcast(&vfsp->vfs_wait);
			}
			spin_unlock(&vfslock);
			return EINTR;
		}
		ASSERT(vfsp->vfs_flag & VFS_MWANT);
		vfsp->vfs_flag &= ~VFS_MWANT;
	}

	ASSERT((vfsp->vfs_flag & (VFS_MLOCK|VFS_OFFLINE)) != VFS_OFFLINE);
	if (vfsp->vfs_flag & VFS_MLOCK) {
		error = EBUSY;
	} else {
		vfsp->vfs_flag |= VFS_MLOCK|flags;
		error = 0;
	}
	spin_unlock(&vfslock);
	return error;
}

/*
 * Lock a filesystem to prevent access to it while mounting.
 * Returns error if already locked.
 */
int
vfs_lock(struct vfs *vfsp)
{
	return (vfs_lock_flags(vfsp, 0));
}

/*
 * Lock a filesystem and mark it offline,
 * to prevent access to it while unmounting.
 * Returns error if already locked.
 */
int
vfs_lock_offline(struct vfs *vfsp)
{
	return (vfs_lock_flags(vfsp, VFS_OFFLINE));
}

/*
 * Unlock a locked filesystem.
 */
void
vfs_unlock(register struct vfs *vfsp)
{
	spin_lock(&vfslock);
	ASSERT((vfsp->vfs_flag & (VFS_MWANT|VFS_MLOCK)) == VFS_MLOCK);
	vfsp->vfs_flag &= ~(VFS_MLOCK|VFS_OFFLINE);

	/*
	 * Wake accessors (traverse() or vfs_syncall())
	 * waiting for the lock to clear.
	 */
	if (vfsp->vfs_flag & VFS_MWAIT) {
		vfsp->vfs_flag &= ~VFS_MWAIT;
		sv_broadcast(&vfsp->vfs_wait);
	}
	spin_unlock(&vfslock);
}

/*
 * Get access permission for vfsp.
 */
int
vfs_busy(struct vfs *vfsp)
{
	spin_lock(&vfslock);
	ASSERT((vfsp->vfs_flag & (VFS_MLOCK|VFS_OFFLINE)) != VFS_OFFLINE);
	while (vfsp->vfs_flag & (VFS_MLOCK|VFS_MWANT)) {
		ASSERT(vfsp->vfs_flag & VFS_MWANT || vfsp->vfs_busycnt == 0);
		if (vfsp->vfs_flag & VFS_OFFLINE) {
			spin_unlock(&vfslock);
			return EBUSY;
		}
		vfsp->vfs_flag |= VFS_MWAIT;
		vfsp_waitsig(vfsp, PVFS, 0);	/* JIMJIM this has no sig "nificance". */
		spin_lock(&vfslock);
	}

	ASSERT(vfsp->vfs_busycnt >= 0);
	vfsp->vfs_busycnt++;
	spin_unlock(&vfslock);

	return 0;
}

/*
 * Given a <dev, filesystem-type> pair, return the vfs-entry for it.
 */
struct vfs *
vfs_busydev(dev_t dev, int type)
{
	struct vfs *vfsp;
	kdev_t kdev = mk_kdev(MAJOR(dev), MINOR(dev));
	struct super_block *sb;

	sb = get_super(kdev);
	if (!sb)
		return NULL;
	
	vfsp = LINVFS_GET_VFS(sb);
again:
	spin_lock(&vfslock);
	if (vfsp->vfs_dev == dev && type == vfsp->vfs_fstype) {
		if (vfsp->vfs_flag & VFS_OFFLINE) {
			spin_unlock(&vfslock);
			drop_super(sb);
			return NULL;
		}
		if (vfsp->vfs_flag & (VFS_MLOCK|VFS_MWANT)) {
			ASSERT(vfsp->vfs_flag & VFS_MWANT ||
			       vfsp->vfs_busycnt == 0);
			vfsp->vfs_flag |= VFS_MWAIT;
			vfsp_wait(vfsp, 0, 0);	 /* JIMJIM removed PZERO */
			goto again;
		}

		ASSERT(vfsp->vfs_busycnt >= 0);
		vfsp->vfs_busycnt++;
	}
	spin_unlock(&vfslock);
	drop_super(sb);
	return vfsp;
}

STATIC void
vfs_unbusy_wakeup(register struct vfs *vfsp)
{
        /*
         * If there's an updater (mount/unmount) waiting for the vfs lock,
         * wake up only it.  Updater should be the first on the sema queue.
         *
         * Otherwise, wake all accessors (traverse() or vfs_syncall())
         * waiting for the lock to clear.
         */
        if (vfsp->vfs_flag & VFS_MWANT) {
                sv_signal(&vfsp->vfs_wait);
        } else if (vfsp->vfs_flag & VFS_MWAIT) {
                vfsp->vfs_flag &= ~VFS_MWAIT;
                sv_broadcast(&vfsp->vfs_wait);
        }
}

void
vfs_unbusy(struct vfs *vfsp)
{
	spin_lock(&vfslock);
	ASSERT(!(vfsp->vfs_flag & (VFS_MLOCK|VFS_OFFLINE)));
	ASSERT(vfsp->vfs_busycnt > 0);
	if (--vfsp->vfs_busycnt == 0)
		vfs_unbusy_wakeup(vfsp);
	spin_unlock(&vfslock);
}

void
vfsinit(void)
{
	/*
	 * Initialize vfs stuff.
	 */
	spin_lock_init(&vfslock);

	/*
	 * Initialize vnode stuff.
	 */
	vn_init();
}

/*
 * Called by fs dependent VFS_MOUNT code to link the VFS base file system 
 * dependent behavior with the VFS virtual object.
 */
void
vfs_insertbhv(
	vfs_t *vfsp, 
	bhv_desc_t *bdp, 
	vfsops_t *vfsops, 	
	void *mount)
{
	/* 
	 * Initialize behavior desc with ops and data and then
	 * attach it to the vfs.
	 */
	bhv_desc_init(bdp, mount, vfsp, vfsops);
	bhv_insert_initial(&vfsp->vfs_bh, bdp);
}