xfs_iget.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 994 行 · 第 1/2 页

C
994
字号
/* * Copyright (c) 2000-2003 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 "xfs_macros.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_dir.h"#include "xfs_dir2.h"#include "xfs_dmapi.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_attr_sf.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_quota.h"#include "xfs_utils.h"/* * Initialize the inode hash table for the newly mounted file system. * * mp -- this is the mount point structure for the file system being *       initialized */voidxfs_ihash_init(xfs_mount_t *mp){	int	i;	mp->m_ihsize = XFS_BUCKETS(mp);	mp->m_ihash = (xfs_ihash_t *)kmem_zalloc(mp->m_ihsize				      * sizeof(xfs_ihash_t), KM_SLEEP);	ASSERT(mp->m_ihash != NULL);	for (i = 0; i < mp->m_ihsize; i++) {		rwlock_init(&(mp->m_ihash[i].ih_lock));	}}/* * Free up structures allocated by xfs_ihash_init, at unmount time. */voidxfs_ihash_free(xfs_mount_t *mp){	kmem_free(mp->m_ihash, mp->m_ihsize*sizeof(xfs_ihash_t));	mp->m_ihash = NULL;}/* * Initialize the inode cluster hash table for the newly mounted file system. * * mp -- this is the mount point structure for the file system being *       initialized */voidxfs_chash_init(xfs_mount_t *mp){	int	i;	/*	 * m_chash size is based on m_ihash	 * with a minimum of 37 entries	 */	mp->m_chsize = (XFS_BUCKETS(mp)) /			 (XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog);	if (mp->m_chsize < 37) {		mp->m_chsize = 37;	}	mp->m_chash = (xfs_chash_t *)kmem_zalloc(mp->m_chsize						 * sizeof(xfs_chash_t),						 KM_SLEEP);	ASSERT(mp->m_chash != NULL);	for (i = 0; i < mp->m_chsize; i++) {		spinlock_init(&mp->m_chash[i].ch_lock,"xfshash");	}}/* * Free up structures allocated by xfs_chash_init, at unmount time. */voidxfs_chash_free(xfs_mount_t *mp){	int	i;	for (i = 0; i < mp->m_chsize; i++) {		spinlock_destroy(&mp->m_chash[i].ch_lock);	}	kmem_free(mp->m_chash, mp->m_chsize*sizeof(xfs_chash_t));	mp->m_chash = NULL;}/* * Look up an inode by number in the given file system. * The inode is looked up in the hash table for the file system * represented by the mount point parameter mp.  Each bucket of * the hash table is guarded by an individual semaphore. * * If the inode is found in the hash table, its corresponding vnode * is obtained with a call to vn_get().  This call takes care of * coordination with the reclamation of the inode and vnode.  Note * that the vmap structure is filled in while holding the hash lock. * This gives us the state of the inode/vnode when we found it and * is used for coordination in vn_get(). * * If it is not in core, read it in from the file system's device and * add the inode into the hash table. * * The inode is locked according to the value of the lock_flags parameter. * This flag parameter indicates how and if the inode's IO lock and inode lock * should be taken. * * mp -- the mount point structure for the current file system.  It points *       to the inode hash table. * tp -- a pointer to the current transaction if there is one.  This is *       simply passed through to the xfs_iread() call. * ino -- the number of the inode desired.  This is the unique identifier *        within the file system for the inode being requested. * lock_flags -- flags indicating how to lock the inode.  See the comment *		 for xfs_ilock() for a list of valid values. * bno -- the block number starting the buffer containing the inode, *	  if known (as by bulkstat), else 0. */STATIC intxfs_iget_core(	vnode_t		*vp,	xfs_mount_t	*mp,	xfs_trans_t	*tp,	xfs_ino_t	ino,	uint		lock_flags,	xfs_inode_t	**ipp,	xfs_daddr_t	bno){	xfs_ihash_t	*ih;	xfs_inode_t	*ip;	xfs_inode_t	*iq;	vnode_t		*inode_vp;	ulong		version;	int		error;	/* REFERENCED */	int		newnode;	xfs_chash_t	*ch;	xfs_chashlist_t	*chl, *chlnew;	SPLDECL(s);	ih = XFS_IHASH(mp, ino);again:	read_lock(&ih->ih_lock);	for (ip = ih->ih_next; ip != NULL; ip = ip->i_next) {		if (ip->i_ino == ino) {			inode_vp = XFS_ITOV_NULL(ip);			if (inode_vp == NULL) {				/* If IRECLAIM is set this inode is				 * on its way out of the system,				 * we need to pause and try again.				 */				if (ip->i_flags & XFS_IRECLAIM) {					read_unlock(&ih->ih_lock);					delay(1);					XFS_STATS_INC(xs_ig_frecycle);					goto again;				}				vn_trace_exit(vp, "xfs_iget.alloc",					(inst_t *)__return_address);				XFS_STATS_INC(xs_ig_found);				ip->i_flags &= ~XFS_IRECLAIMABLE;				read_unlock(&ih->ih_lock);				XFS_MOUNT_ILOCK(mp);				list_del_init(&ip->i_reclaim);				XFS_MOUNT_IUNLOCK(mp);				goto finish_inode;			} else if (vp != inode_vp) {				struct inode *inode = LINVFS_GET_IP(inode_vp);				/* The inode is being torn down, pause and				 * try again.				 */				if (inode->i_state & (I_FREEING | I_CLEAR)) {					read_unlock(&ih->ih_lock);					delay(1);					XFS_STATS_INC(xs_ig_frecycle);					goto again;				}/* Chances are the other vnode (the one in the inode) is being torn * down right now, and we landed on top of it. Question is, what do * we do? Unhook the old inode and hook up the new one? */				cmn_err(CE_PANIC,			"xfs_iget_core: ambiguous vns: vp/0x%p, invp/0x%p",						inode_vp, vp);			}			read_unlock(&ih->ih_lock);			XFS_STATS_INC(xs_ig_found);finish_inode:			if (lock_flags != 0) {				xfs_ilock(ip, lock_flags);			}			newnode = (ip->i_d.di_mode == 0);			if (newnode) {				xfs_iocore_inode_reinit(ip);			}			ip->i_flags &= ~XFS_ISTALE;			vn_trace_exit(vp, "xfs_iget.found",						(inst_t *)__return_address);			goto return_ip;		}	}	/*	 * Inode cache miss: save the hash chain version stamp and unlock	 * the chain, so we don't deadlock in vn_alloc.	 */	XFS_STATS_INC(xs_ig_missed);	version = ih->ih_version;	read_unlock(&ih->ih_lock);	/*	 * Read the disk inode attributes into a new inode structure and get	 * a new vnode for it. This should also initialize i_ino and i_mount.	 */	error = xfs_iread(mp, tp, ino, &ip, bno);	if (error) {		return error;	}	vn_trace_exit(vp, "xfs_iget.alloc", (inst_t *)__return_address);	xfs_inode_lock_init(ip, vp);	xfs_iocore_inode_init(ip);	if (lock_flags != 0) {		xfs_ilock(ip, lock_flags);	}	/*	 * Put ip on its hash chain, unless someone else hashed a duplicate	 * after we released the hash lock.	 */	write_lock(&ih->ih_lock);	if (ih->ih_version != version) {		for (iq = ih->ih_next; iq != NULL; iq = iq->i_next) {			if (iq->i_ino == ino) {				write_unlock(&ih->ih_lock);				xfs_idestroy(ip);				XFS_STATS_INC(xs_ig_dup);				goto again;			}		}	}	/*	 * These values _must_ be set before releasing ihlock!	 */	ip->i_hash = ih;	if ((iq = ih->ih_next)) {		iq->i_prevp = &ip->i_next;	}	ip->i_next = iq;	ip->i_prevp = &ih->ih_next;	ih->ih_next = ip;	ip->i_udquot = ip->i_gdquot = NULL;	ih->ih_version++;	write_unlock(&ih->ih_lock);	/*	 * put ip on its cluster's hash chain	 */	ASSERT(ip->i_chash == NULL && ip->i_cprev == NULL &&	       ip->i_cnext == NULL);	chlnew = NULL;	ch = XFS_CHASH(mp, ip->i_blkno); chlredo:	s = mutex_spinlock(&ch->ch_lock);	for (chl = ch->ch_list; chl != NULL; chl = chl->chl_next) {		if (chl->chl_blkno == ip->i_blkno) {			/* insert this inode into the doubly-linked list			 * where chl points */			if ((iq = chl->chl_ip)) {				ip->i_cprev = iq->i_cprev;				iq->i_cprev->i_cnext = ip;				iq->i_cprev = ip;				ip->i_cnext = iq;			} else {				ip->i_cnext = ip;				ip->i_cprev = ip;			}			chl->chl_ip = ip;			ip->i_chash = chl;			break;		}	}	/* no hash list found for this block; add a new hash list */	if (chl == NULL)  {		if (chlnew == NULL) {			mutex_spinunlock(&ch->ch_lock, s);			ASSERT(xfs_chashlist_zone != NULL);			chlnew = (xfs_chashlist_t *)					kmem_zone_alloc(xfs_chashlist_zone,						KM_SLEEP);			ASSERT(chlnew != NULL);			goto chlredo;		} else {			ip->i_cnext = ip;			ip->i_cprev = ip;			ip->i_chash = chlnew;			chlnew->chl_ip = ip;			chlnew->chl_blkno = ip->i_blkno;			chlnew->chl_next = ch->ch_list;			ch->ch_list = chlnew;			chlnew = NULL;		}	} else {		if (chlnew != NULL) {			kmem_zone_free(xfs_chashlist_zone, chlnew);		}	}	mutex_spinunlock(&ch->ch_lock, s);	/*	 * Link ip to its mount and thread it on the mount's inode list.	 */	XFS_MOUNT_ILOCK(mp);	if ((iq = mp->m_inodes)) {		ASSERT(iq->i_mprev->i_mnext == iq);		ip->i_mprev = iq->i_mprev;		iq->i_mprev->i_mnext = ip;		iq->i_mprev = ip;		ip->i_mnext = iq;	} else {		ip->i_mnext = ip;		ip->i_mprev = ip;	}	mp->m_inodes = ip;	XFS_MOUNT_IUNLOCK(mp);	newnode = 1; return_ip:	ASSERT(ip->i_df.if_ext_max ==	       XFS_IFORK_DSIZE(ip) / sizeof(xfs_bmbt_rec_t));	ASSERT(((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) != 0) ==	       ((ip->i_iocore.io_flags & XFS_IOCORE_RT) != 0));	*ipp = ip;	/*	 * If we have a real type for an on-disk inode, we can set ops(&unlock)	 * now.	 If it's a new inode being created, xfs_ialloc will handle it.	 */	VFS_INIT_VNODE(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1);	return 0;}/* * The 'normal' internal xfs_iget, if needed it will * 'allocate', or 'get', the vnode. */intxfs_iget(	xfs_mount_t	*mp,	xfs_trans_t	*tp,	xfs_ino_t	ino,	uint		lock_flags,	xfs_inode_t	**ipp,	xfs_daddr_t	bno){	struct inode	*inode;	vnode_t		*vp = NULL;	int		error;retry:	XFS_STATS_INC(xs_ig_attempts);	if ((inode = iget_locked(XFS_MTOVFS(mp)->vfs_super, ino))) {		bhv_desc_t	*bdp;		xfs_inode_t	*ip;		int		newnode;		vp = LINVFS_GET_VP(inode);		if (inode->i_state & I_NEW) {inode_allocate:			vn_initialize(inode);			error = xfs_iget_core(vp, mp, tp, ino,						lock_flags, ipp, bno);			if (error) {				vn_mark_bad(vp);				if (inode->i_state & I_NEW)					unlock_new_inode(inode);				iput(inode);			}		} else {			/* These are true if the inode is in inactive or			 * reclaim. The linux inode is about to go away,			 * wait for that path to finish, and try again.			 */			if (vp->v_flag & (VINACT | VRECLM)) {				vn_wait(vp);				iput(inode);				goto retry;			}			bdp = vn_bhv_lookup(VN_BHV_HEAD(vp), &xfs_vnodeops);			if (bdp == NULL) {				XFS_STATS_INC(xs_ig_dup);				goto inode_allocate;			}			ip = XFS_BHVTOI(bdp);			if (lock_flags != 0)				xfs_ilock(ip, lock_flags);			newnode = (ip->i_d.di_mode == 0);			if (newnode)				xfs_iocore_inode_reinit(ip);			XFS_STATS_INC(xs_ig_found);			*ipp = ip;			error = 0;		}	} else		error = ENOMEM;	/* If we got no inode we are out of memory */	return error;}/*

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?