/*
* Copyright (c) 2000-2005 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 <linux/delay.h>
#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"
#include "xfs_bit.h"
/*
* Initialize the inode hash table for the newly mounted file system.
* Choose an initial table size based on user specified value, else
* use a simple algorithm using the maximum number of inodes as an
* indicator for table size, and clamp it between one and some large
* number of pages.
*/
void
xfs_ihash_init(xfs_mount_t *mp)
{
__uint64_t icount;
uint i, flags = KM_SLEEP | KM_MAYFAIL;
if (!mp->m_ihsize) {
icount = mp->m_maxicount ? mp->m_maxicount :
(mp->m_sb.sb_dblocks << mp->m_sb.sb_inopblog);
mp->m_ihsize = 1 << max_t(uint, 8,
(xfs_highbit64(icount) + 1) / 2);
mp->m_ihsize = min_t(uint, mp->m_ihsize,
(64 * NBPP) / sizeof(xfs_ihash_t));
}
while (!(mp->m_ihash = (xfs_ihash_t *)kmem_zalloc(mp->m_ihsize *
sizeof(xfs_ihash_t), flags))) {
if ((mp->m_ihsize >>= 1) <= NBPP)
flags = KM_SLEEP;
}
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.
*/
void
xfs_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.
* Its size is derived from the ihash table size.
*/
void
xfs_chash_init(xfs_mount_t *mp)
{
uint i;
mp->m_chsize = max_t(uint, 1, mp->m_ihsize /
(XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog));
mp->m_chsize = min_t(uint, mp->m_chsize, mp->m_ihsize);
mp->m_chash = (xfs_chash_t *)kmem_zalloc(mp->m_chsize
* sizeof(xfs_chash_t),
KM_SLEEP);
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.
*/
void
xfs_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;
}
/*
* Try to move an inode to the front of its hash list if possible
* (and if its not there already). Called right after obtaining
* the list version number and then dropping the read_lock on the
* hash list in question (which is done right after looking up the
* inode in question...).
*/
STATIC void
xfs_ihash_promote(
xfs_ihash_t *ih,
xfs_inode_t *ip,
ulong version)
{
xfs_inode_t *iq;
if ((ip->i_prevp != &ih->ih_next) && write_trylock(&ih->ih_lock)) {
if (likely(version == ih->ih_version)) {