Files
seta75D d6fe8fe829 Init
2021-10-11 22:19:34 -03:00

1978 lines
39 KiB
C

static char sccsid[] = "@(#)19 1.63.1.36 src/bos/kernel/pfs/isubs.c, syspfs, bos41J, 9516A_all 4/17/95 13:17:19";
/*
* COMPONENT_NAME: (SYSPFS) Physical File System
*
* FUNCTIONS: ifind, iget, _iget, ilock, ilocklst, imark,
* inoinit, iput, irele, iunhash, iactivity,
* iuncache, getip
*
* ORIGINS: 3, 26, 27
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
* (C) COPYRIGHT International Business Machines Corp. 1988, 1995
* All Rights Reserved
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*/
#include "jfs/jfslock.h"
#include "jfs/fsvar.h"
#include "jfs/commit.h"
#include "sys/errno.h"
#include "sys/vfs.h"
#include "sys/sleep.h"
#include "sys/malloc.h"
#include "sys/syspest.h"
#include "sys/sysinfo.h"
#include "vmm/vmsys.h"
/*
* inode cache
*/
int maxicache; /* Maximum # of inodes in system */
int maxcachesize; /* Maximum # of inodes allowed in cache */
int cachesize; /* Number of inodes currently in cache */
struct genalloc *ino_ap; /* Inode allocation pointer */
/*
* inode lru cache list
*
* offsets of ic_next and ic_prev must be same as in the inode
*/
struct {
struct inode *ic_forw; /* hash list offset, not used */
struct inode *ic_back;
struct inode *ic_next; /* Cachelist offsets */
struct inode *ic_prev;
} icachelist;
/*
* inode hash list anchor table
*/
int nhino;
struct hinode *hinode;
/* inode cache queue management macros */
#define INSERT_HASH(e, after) insque(e, after)
#define REMOVE_HASH(e) remque(e)
#define NULL_HASH(e) \
((struct inode *)(e))->i_forw = \
((struct inode *)(e))->i_back = ((struct inode *)(e))
#define INSERT_CACHE(e, after) insque2(e, after)
#define REMOVE_CACHE(e) remque2(e)
#define NULL_CACHE(e) \
((struct inode *)(e))->i_next = \
((struct inode *)(e))->i_prev = ((struct inode *)(e))
/*
* inode cache lock
*/
Simple_lock jfs_icache_lock;
int ilock_cnt = 0; /* inode lock occurrence */
int vlock_cnt = 1<<15; /* vnode lock occurrence */
BUGVDEF(isubsdbg, 0)
/*
* combined vnode/inode for base (non-shadow) objects
*/
struct xnode {
struct vnode x_vnode;
struct inode x_inode;
};
/*
* NAME: inoinit ()
*
* FUNCTION: Initialize xnode (vnode+inode) table
*
* PARAMETERS: None
*
* RETURN : None
*
*/
inoinit()
{
extern struct fsvar fsv;
extern int dqcachemax;
struct hinode *hip;
struct inode *ip;
int n, rc, i;
uint memsize;
extern uint pfs_memsize();
uint k;
int numinodes;
/* Initialize the inode cache lock */
lock_alloc(&jfs_icache_lock,LOCK_ALLOC_PAGED,ICACHE_LOCK_CLASS,-1);
simple_lock_init(&jfs_icache_lock);
/* allocate the inode hash table. scale the number of hash
* buckets with memory size by the following:
* 1. for machines with 64M and less of memory will have 512 hash
* buckets.
* 2. every 64M of memory after will have a log 2 number of buckets.
*/
memsize = pfs_memsize();
/* round up memsize to the nearest Meg */
memsize = (memsize + ((1024*1024)-1) & ~(1024*1024));
if (memsize < 64*1024*1024)
{
nhino = 512;
}
else
{
k = memsize/(64*1024*1024);
for (i = 0; i < 32; i++)
{
if (k & 0x80000000)
break;
k <<= 1;
}
nhino = (1 << (31-i)) * 512;
}
if ((hinode = (struct hinode *) malloc(nhino * sizeof(struct hinode))) == NULL)
panic("Inoinit: unable to allocate inode hash table");
/* max inactive inodes before LRU recycle of them:
* ((number of xnode table pages allocated per Mbyte of real memory) *
* (number of xnodes per page)) where
* the number of xnode table pages allocated per Mbyte of real memory
* for real memory < 32Mbyte: 2
* for real memory >= 32Mbyte: 4
*
* table size (max active inodes):
* 2 * OPEN_MAX + max cache size
* (ref. OPEN_MAX in limits.h)
*/
if (memsize < 32*1024*1024)
maxicache = (memsize / 0x80000) * (PSIZE / sizeof(struct xnode));
else
maxicache = (memsize / 0x40000) * (PSIZE / sizeof(struct xnode));
rc = geninit((numinodes=2 * OPEN_MAX + maxicache), fsv.v_ninode,
sizeof(struct xnode), (caddr_t)&ip->i_forw - (caddr_t)ip,
"INODES", &fsv.v_inode);
if (rc)
panic("Inoinit: unable to allocate inode table");
/* Number of dquots to allocate, worst case 2 per inode */
dqcachemax=2 * numinodes;
/* Save inode allocation pointer */
ino_ap = fsv.v_inode;
/* Limit maximum inode cache size to initial allocation */
maxcachesize = fsv.v_ninode;
/* initialize the hash table */
for (hip = hinode; hip < &hinode[nhino]; hip++)
{ hip->hi_forw = (struct inode *)hip;
hip->hi_back = (struct inode *)hip;
hip->hi_timestamp = 0;
hip->hi_vget = 0;
}
/* Null hash icachelist */
icachelist.ic_next = (struct inode *) &icachelist;
icachelist.ic_prev = (struct inode *) &icachelist;
return 0;
}
/*
* NAME: inogrow ()
*
* FUNCTION: Allow inode table to grow
*
* EXECUTION ENVIRONMENT: called durring phase 2 system initialization.
*
* PARAMETERS: None
*
* RETURN : None
*
*/
void
inogrow()
{
maxcachesize = maxicache;
}
/*
* NAME: imark (ip, flag)
*
* FUNCTION: update i_flag and accessed, changed, or updated times
* in an inode.
*
* PARAMETERS: ip - pointer to inode
* flag - inode times to set
*
* RETURN : None
*
* NOTE: readers hold INODE_LOCK() to serialize update of i_atime
* on entry/exit
*/
imark(ip, flag)
struct inode *ip;
int flag;
{
void curtime(struct timestruc_t *);
struct timestruc_t t;
if (isreadonly(ip))
return;
curtime(&t); /* fetch current time */
ip->i_flag |= flag;
if (flag & IACC)
ip->i_atime_ts = t;
if (flag & IUPD)
ip->i_mtime_ts = t;
if (flag & ICHG)
ip->i_ctime_ts = t;
}
/*
* NAME: iwritelocklist(n, va_ilist)
*
* FUNCTION: Lock variable number of inodes in descending i_number
*
* PARAMETERS: n - number of inodes
* va_ilist - varags inode list
*
* RETURN : None
*
*/
iwritelocklist(n, va_ilist)
int n; /* Number of elements */
va_list va_ilist; /* Varargs list */
{
struct inode **ipp, *ip;
int k, changes;
struct inode *ipl[6];
ipp = (struct inode **) &va_ilist;
for (k = 0; k < n; k++)
ipl[k] = *ipp++;
/* Bubble sort */
do
{
changes = 0;
for (k = 0; k < n; k++)
if ((k+1) < n && ipl[k+1]->i_number > ipl[k]->i_number)
{
ip = ipl[k];
ipl[k] = ipl[k+1];
ipl[k+1] = ip;
changes++;
}
} while (changes);
for (k = 0; k < n; k++)
IWRITE_LOCK(ipl[k]);
}
/*
* NAME: ireadlockx(ip)
*
* FUNCTION: wrapper to lock inode
*
* PARAMETERS: ip - inode we want to lock
*
* RETURN : None
*
*/
ireadlockx(ip)
struct inode *ip;
{
IREAD_LOCK(ip);
}
/*
* NAME: ireadunlockx(ip)
*
* FUNCTION: wrapper to unlock inode
*
* PARAMETERS: ip - inode we want to unlock
*
* RETURN : None
*
*/
ireadunlockx(ip)
struct inode *ip;
{
IREAD_UNLOCK(ip);
}
/*
* NAME: iwritelockx(ip)
*
* FUNCTION: wrapper to lock inode
*
* PARAMETERS: ip - inode we want to lock
*
* RETURN : None
*
*/
iwritelockx(ip)
struct inode *ip;
{
IWRITE_LOCK(ip);
}
/*
* NAME: iwriteunlockx(ip)
*
* FUNCTION: wrapper to unlock inode
*
* PARAMETERS: ip - inode we want to unlock
*
* RETURN : None
*
*/
iwriteunlockx(ip)
struct inode *ip;
{
IWRITE_UNLOCK(ip);
}
/*
* NAME: ifind (dev)
*
* FUNCTION: find the specified inode which MUST exist in
* the inode cache.
*
* PARAMETERS: dev - device of wanted inode
*
* RETURN : PANIC if the specified inode is not found.
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit
*/
struct inode *
ifind (dev)
dev_t dev; /* device on which inode resides */
{
struct inode *ip;
struct hinode *hip;
IHASH(0, dev, hip);
/* Search the hash list for this inode
*/
for (ip = hip->hi_forw; ip != (struct inode *) hip; ip = ip->i_forw)
if (ip->i_number == 0 && dev == ip->i_dev)
return ip;
panic("ifind: failure");
}
/* NAME: iget (dev, ino, ipp, doscan, vsfp)
*
* FUNCTION: This function is an intermediate function to the actual
* iget() function call, now named _iget(). This function
* preserves the old semantics of iget() for other filesystems
* such as AFS which make calls to iget().
*/
iget (dev, ino, ipp, doscan, vfsp)
dev_t dev;
ino_t ino;
struct inode **ipp;
int doscan;
struct vfs *vfsp;
{
struct hinode *hip;
sysinfo.iget++;
cpuinfo[CPUID].iget++;
IHASH(ino, dev, hip);
return(_iget(dev, ino, hip, ipp, doscan, vfsp));
}
/*
* NAME: _iget (dev, ino, hip, ipp, doscan, vfsp)
*
* FUNCTION: get the specified inode where
* if vfsp != NULL, get also the vnode of the inode,
* if vfsp == NULL, get only the inode.
*
* if doscan == 1, we need to scan the hash table for the inode.
* if doscan == 0, this is a new inode and we need not scan hash.
*
* reference of vnode and/or inode is acquired but not locked.
*
* PARAMETERS: dev - device of wanted inode
* ino - inode number
* ipp - returned inode
* doscan - new inode, no scan needed
* vfsp - vfs
*
* RETURN : ENFILE - If out of inodes
* errors from subroutines
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit
*/
_iget (dev, ino, hip, ipp, doscan, vfsp)
dev_t dev; /* Device for ino */
ino_t ino; /* Establish ic inode for ino # */
struct hinode *hip; /* hash list where inode resides */
struct inode **ipp; /* Return addr for found/new ip */
int doscan; /* Is this a newly created inode? */
struct vfs *vfsp; /* Is this a device mount? */
{
int rc;
struct inode *ip;
struct gnode *gp;
struct vnode *vp;
int gen, type;
extern struct vnodeops jfs_vops;
extern time_t time; /* current time for new inode i_gen */
extern gn_reclk_count; /* occurrence number of gn_reclk lock */
int cachelist; /* is the inode from the cachelist? */
int timestamp; /* timestamp on hash chain modification */
int active; /* number of active references on a vnode */
if (vfsp)
ASSERT(vfsp->vfs_flag & VFS_DEVMOUNT);
if (doscan == 0)
{
*ipp = NULL;
goto getinode;
}
loop:
*ipp = NULL;
/* Search the hash list for the specified inode
*/
for (ip = hip->hi_forw; ip != (struct inode *) hip; ip = ip->i_forw)
if (ino == ip->i_number && dev == ip->i_dev)
{
/*
* cache hit
*
* if IXLOCK is set, the inode is in transition
* (either being initialized or recycled/freed),
* wait for completion of transition.
* if i_mode == 0, the inode found has transformed
* as invalid and to be discarded.
*/
ip->i_count++;
if (ip->i_locks & IXLOCK)
{
do {
ip->i_locks |= IXWANT;
e_sleep_thread(&ip->i_event,
&jfs_icache_lock,
LOCK_SIMPLE);
if (ip->i_mode == 0)
{
iput(ip, NULL);
goto loop;
}
} while (ip->i_locks & IXLOCK);
goto getvnode;
}
/* remove from cachelist if it is being reactivated.
*/
if (ip->i_count == 1)
{
REMOVE_CACHE(ip);
NULL_CACHE(ip);
cachesize--;
}
getvnode:
/*
* get vnode (inode cache hit)
*
* v_count specifies references of the vnode
* while i_count specifies number of vnodes
* referencing the base inode.
* (vnode management for soft mount (vfsp == NULL)
* is provided by LFS).
*/
if (vfsp)
{
vp = ip->i_gnode.gn_vnode;
active = vp->v_count;
fetch_and_add(&vp->v_count, 1);
if (active)
{
/* vnode had been already activated.
*/
ip->i_count--;
}
else
{
/* either reactivated from cachelist
* (vnode stayed linked to underlying
* fs: v_vfsp != NULL) or
* object in dir-over-dir mount is
* being accessed in underlying fs after
* being accessed in mounted fs first
* (base vnode had not been linked to
* underlying fs: v_vfsp == NULL)
*/
if (vp->v_vfsp == NULL)
vget(ip, vfsp);
}
}
*ipp = ip;
return 0;
}
/*
* cache miss
*/
/* get an in-memory inode.
* 1. set the timestamp on the hash list.
* 2. if we don't release the ICACHE_LOCK() while in getip()
* we simply continue.
* 3. if we release the lock, we must check the timestamp.
* a. timestamp not equal means something was inserted or
* deleted, therefore we have to search again.
* b. timestamp is equal we just try to get an inode again.
*/
getinode:
timestamp = hip->hi_timestamp;
switch (rc = getip(&ip, &cachelist))
{
case 0:
break;
case 1:
if (timestamp != hip->hi_timestamp)
goto loop;
else
goto getinode;
case ENFILE:
return rc;
}
/*
* initialize inode
*
* either a clean vnode+inode is allocated from freelist
* or a vnode+inode has been recycled from cachelist.
*/
/* initialize inode before inserting into hash list so that
* concurrent _iget()s for this inode will wait on it.
*/
ip->i_dev = dev;
ip->i_number = ino;
ip->i_count = 1;
ip->i_locks |= IXLOCK;
ip->i_event = EVENT_NULL;
/* insert the inode at head of hash list
*/
INSERT_HASH(ip, hip);
NULL_CACHE(ip);
/* increment the timestamp on the hash
*/
hip->hi_timestamp++;
/* get mount inode for the inode
*/
ip->i_ipmnt = ifind(ip->i_dev);
/* if the inode was recycled from the cache list,
* cleanup dquots of previous object.
*/
if (cachelist)
putinoquota(ip);
/* read on-disk inode into in-memory inode
*/
ICACHE_UNLOCK();
rc = iread(ip);
ICACHE_LOCK();
/* get dquots for the inode if not newly created object.
* (for newly created object, caller will get and check
* inode quota)
*/
if (rc == 0 && ip->i_nlink != 0)
rc = getinoquota(ip);
/* if initialization failed, free the inode.
*/
if (rc)
{
/* mark the inode to be discarded and remove it from
* hashlist to prevent later _iget() to find the inode.
*/
ip->i_mode = 0;
REMOVE_HASH(ip);
NULL_HASH(ip);
/* wakeup sleepers (concurrent _iget() who found the inode)
* who will iput() the inode on i_mode = 0. on the last
* reference release, the inode will be freed.
*/
ip->i_locks &= ~IXLOCK;
if (ip->i_locks & IXWANT)
{
ip->i_locks &= ~IXWANT;
e_wakeupx(&ip->i_event, E_WKX_NO_PREEMPT);
}
iput(ip, NULL);
return rc;
}
/*
* continue to initialize the inode (should not fail.)
*/
gp = ITOGP(ip);
/* if newly created inode (i_nlink = 0), initialize/update
* i_gen and zero out all other on-disk inode fields
* (caller of new inode creation (dev_ialloc()) will initialize
* further from creation request parameters).
* otherwise initialize further based on on-disk inode information.
*/
if (ip->i_nlink == 0)
{
/* If the inode has never been allocated,
* start the gen at the current time.
*/
if (ip->i_gen == 0)
gen = time;
else
gen = ip->i_gen + 1;
bzero(&ip->i_dinode, sizeof(ip->i_dinode));
ip->i_gen = gen;
}
else
{
gp->gn_type = IFTOVT(ip->i_mode);
/* for regular files and directories and big symbolic links
* set indirect block page pointer to zero
*/
type = ip->i_mode & IFMT;
switch (type) {
case IFLNK:
if (ip->i_size <= D_PRIVATE)
break;
case IFREG:
case IFDIR:
ip->i_vindirect = 0;
break;
default:
break;
}
if (gp->gn_type == VCHR || gp->gn_type == VBLK)
gp->gn_rdev = ip->i_rdev;
else
gp->gn_rdev = ip->i_dev;
ip->i_flag = 0;
}
ip->i_cflag = (ip->i_nlink) ? 0 : CMNEW;
ip->i_compress = 0;
ip->i_movedfrag = NULL;
ip->i_cluster = 0;
if (!(ip->i_locks & ILOCKALLOC))
{
ilock_cnt++;
#ifdef _I_MULT_RDRS
lock_alloc(&ip->i_rdwrlock,LOCK_ALLOC_PAGED,IRDWR_LOCK_CLASS,
ilock_cnt);
lock_init(&ip->i_rdwrlock, 1);
lock_alloc(&ip->i_nodelock,LOCK_ALLOC_PAGED,INODE_LOCK_CLASS,
ilock_cnt);
simple_lock_init(&ip->i_nodelock);
#else /* simple lock */
lock_alloc(&ip->i_rdwrlock,LOCK_ALLOC_PAGED,IRDWR_LOCK_CLASS,
ilock_cnt);
simple_lock_init(&ip->i_rdwrlock);
#endif /* _I_MULT_RDRS */
ip->i_locks |= ILOCKALLOC;
}
ip->i_openevent = EVENT_NULL;
/* fill in the remaining gnode fields for this inode
*/
gp->gn_flags = 0;
gp->gn_ops = &jfs_vops;
gp->gn_seg = 0;
gp->gn_mwrcnt = gp->gn_mrdcnt = 0;
gp->gn_rdcnt = gp->gn_wrcnt = gp->gn_excnt = gp->gn_rshcnt = 0;
if (!(ip->i_locks & GLOCKALLOC))
{
lock_alloc(&gp->gn_reclk_lock,LOCK_ALLOC_PAGED,RECLK_LOCK_CLASS,
gn_reclk_count++);
simple_lock_init(&gp->gn_reclk_lock);
ip->i_locks |= GLOCKALLOC;
}
gp->gn_reclk_event = EVENT_NULL;
gp->gn_filocks = (struct filock *)NULL;
gp->gn_data = (caddr_t)ip;
/* get the vnode (on inode cache miss)
*/
vget(ip, vfsp);
/* wakeup sleepers (concurrent _iget() who found the inode)
*/
ip->i_locks &= ~IXLOCK;
if (ip->i_locks & IXWANT)
{
ip->i_locks &= ~IXWANT;
e_wakeupx(&ip->i_event, E_WKX_NO_PREEMPT);
}
*ipp = ip;
return 0;
}
/*
* NAME: getip(ipp, cachelist)
*
* FUNCTION: allocate a in-memory inode.
*
* PARAMETERS: ipp - returned inode
* cachelist - set to 1 if inode allocated from cache list
* set to 0 if inode allocated from free list
*
* RETURN : 0 - success without ICACHE_LOCK() has been released
* 1 - success with ICACHE_LOCK() has been temporarily
* released
* ENFILE - inode table overflow
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit
*/
static
getip(ipp, cachelist)
struct inode **ipp;
int *cachelist;
{
struct inode *ip;
struct vnode *vp;
struct xnode *xp;
struct hinode *hip;
*ipp = NULL;
/* if cachesize is under the max cachesize
* try to allocate from the free list.
*/
if (cachesize < maxcachesize)
{
if ((xp = (struct xnode *) genalloc(ino_ap)) != NULL)
{
/* bind inode and vnode */
ip = (struct inode *)&xp->x_inode;
ip->i_locks = 0;
vp = (struct vnode *)&xp->x_vnode;
vp->v_vfsp = NULL;
vp->v_next = NULL;
ip->i_gnode.gn_vnode = (struct vnode *)vp;
vp->v_gnode = (struct gnode*)&ip->i_gnode;
*ipp = ip;
*cachelist = 0;
return 0;
}
}
/* either cachesize reached cachesize limit or
* free list is empty: try to recycle from cache list.
* first check if there is anything on the cachelist.
*/
if ((ip = icachelist.ic_next) == (struct inode *) &icachelist)
return ENFILE;
/* if ICACHE_LOCK() had been temporarily released,
* rescan hashlist whether the specified inode had been entered
* by another thread.
*/
if (iuncache(ip))
{
/* if no _iget()s are pending on the inode deactivated.
* insert back at head of cachelist;
* otherwise, yield the inode to holder of reference.
*/
if (ip->i_count == 0)
{
cachesize++;
INSERT_CACHE(ip, &icachelist);
}
return 1;
}
/* remove from previous hash chain
*/
REMOVE_HASH(ip);
*ipp = ip;
*cachelist = 1;
return 0;
}
/*
* NAME: iuncache (ip)
*
* FUNCTION: deactivate the specified inode to recycle:
* remove inode from cache list,
* commit changes of inode and/or file, and/or
* release its virtual memory resources.
*
* the lock resources are reused, and
* linkage with quota struct is maintained.
*
* PARAMETERS: ip - inode to blast
*
* RETURN : 0 - if ICACHE_LOCK() has NOT been temporarily released.
* 1 - if ICACHE_LOCK() has been temporarily released.
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit
*/
iuncache (ip)
struct inode *ip;
{
int sid;
/* remove from cache list.
*/
if (ip->i_count == 0)
{
ASSERT(ip != ip->i_next);
cachesize--;
REMOVE_CACHE(ip);
NULL_CACHE(ip);
}
/* if the inode is ready to be recycled,
* unlink vnode from previous vfs.
*/
sid = ip->i_seg;
if (sid == 0 &&
(!(ip->i_flag & (IACC|ICHG|IFSYNC)) ||
isreadonly(ip)))
{
vput(ip);
return 0;
}
/* mark the inode as in transition so that _iget() of this inode
* will wait for the completion of transition.
*/
ip->i_locks |= IXLOCK;
ip->i_count++;
ICACHE_UNLOCK();
/* Mark its vm object inactive so further page faults due to stale
* references will fail: actual delete will occur when the last
* reference is released.
*/
if (sid != 0)
vms_inactive(sid);
/* if inode and/or file has changed then commit it
*/
if (ip->i_number > SPECIAL_I || ip->i_number == ROOTDIR_I)
{
/* for regular files, mark to be synced
* if file have been modified
*/
if (sid &&
((ip->i_mode & IFMT) == IFREG) &&
(ip->i_flag & IUPD || ismodified(ip)))
ip->i_flag |= IFSYNC;
/* commit if anything has been changed
*/
if (ip->i_flag & (IACC|ICHG|IFSYNC))
{
IWRITE_LOCK(ip);
commit(1, ip);
IWRITE_UNLOCK(ip);
}
}
/* flush segment if journalled
*/
if (ip->i_mode & IFJOURNAL)
iflush(ip);
/* release vm resources associated with the inode.
*/
if (sid != 0)
{
/* delete vm object: actual delete will occur when
* the last reference is released.
*/
isegdel(ip);
/* flush and free its indirect block pages in .indirect segment
*/
if (ip->i_vindirect)
ifreeind(ip);
}
ICACHE_LOCK();
ip->i_count--;
/* wakeup sleepers (_iget()s for this inode who found it).
* yield the uncached inode to the _iget() for this inode.
*/
ip->i_locks &= ~IXLOCK;
if (ip->i_locks & IXWANT)
{
ip->i_locks &= ~IXWANT;
e_wakeupx(&ip->i_event, E_WKX_NO_PREEMPT);
}
return 1;
}
/*
* NAME: iput (ip, vfsp)
*
* FUNCTION: put the specified inode where
* if vfsp != NULL, put also the vnode of the inode,
* if vfsp == NULL, put only the inode.
*
* reference of vnode and/or inode is released
* on the last reference release, insert into cachelist
* (valid inode) or return to freelist (invalid inode).
*
* PARAMETERS: ip - unwanted inode
* vfsp - vfs
*
* RETURN : Errors from subroutines.
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit
*/
iput(ip, vfsp)
struct inode *ip;
struct vfs *vfsp;
{
struct vnode *vp;
assert(ip->i_count > 0);
/* release vnode reference
*/
if (vfsp)
{
vp = ip->i_gnode.gn_vnode;
assert(vp->v_count > 0);
fetch_and_add(&vp->v_count, -1);
if (vp->v_count)
return 0;
}
/* release inode reference
*/
if (--ip->i_count > 0)
return 0;
/*
* last reference release
*/
ip->i_count = 0;
/* If iput() is for an inode for which no "real"
* file exists don't re-deallocate the file!
*/
if (ip->i_mode && ((ip->i_nlink == 0)
|| (ip->i_flag & IDEFER) || ip->i_compress))
{
/* mark the inode as in transition so that _iget() of
* this inode will wait for the completion of transition.
*/
ip->i_locks |= IXLOCK;
/* cleanup resources or fsync as required.
*/
iclose(ip);
/* wakeup sleepers (_iget()s for this inode who found it).
* yield the inode to the _iget() for this inode.
*/
ip->i_locks &= ~IXLOCK;
if (ip->i_locks & IXWANT)
{
/* if the inode is being deleted,
* mark the inode to be discarded by previous
* _iget()s who found the inode, and remove it from
* hashlist to prevent later _iget() to find the
* inode. (e.g., VNOP_VGET())
*/
if (ip->i_nlink == 0)
{
ip->i_mode = 0;
ip->i_gen++;
REMOVE_HASH(ip);
NULL_HASH(ip);
}
ip->i_locks &= ~IXWANT;
e_wakeupx(&ip->i_event, E_WKX_NO_PREEMPT);
return 0;
}
}
/* insert at tail of cache list if it can be reactivated
*/
if (ip->i_nlink && ip->i_mode && !(ip->i_flag & IDEFER))
{
ip->i_flag &= (ICHG|IUPD|IACC|IFSYNC);
cachesize++;
INSERT_CACHE(ip, icachelist.ic_prev);
return 0;
}
/* unhash inode and return to free list if should not
* be reactivated
*/
iunhash(ip);
return 0;
}
/*
* NAME: iclose (ip)
*
* FUNCTION: Close processing for a segment when i_count becomes
* zero. this procedure is called from iput.
*
* (1) if the i_nlink field is zero, the resources
* associated with the inode are freed, including the
* inode itself.
*
* (2) if it is a compressed file which has been
* modified, i/o is initiated to write it out,
* provided the flag vmker.noflush is 0.
*
* PARAMETERS: ip - pointer to inode to close
*
* RETURN : errors from subroutines
*
* SERIALIZATION: called only by iput() with ICACHE_LOCK() and
* IXLOCK on the inode
*/
static
iclose (ip)
struct inode *ip;
{
int sid, rc , sr12save;
label_t jbuf;
/* clean up resources for inode with i_nlink = 0.
* permanent disk map and inode map should already have
* been taken care of by ctrunc() (i.e, at commit() with
* i_nlink = 0) or by itrunc().
*/
if (ip->i_nlink == 0)
{
/* mark the inode to be skipped in logsync()
*/
ip->i_cflag |= ICLOSE;
ICACHE_UNLOCK();
if (ip->i_seg != 0)
{
sr12save = mfsr(12);
if (rc = setjmpx(&jbuf))
{
(void)chgsr(12,sr12save);
ICACHE_LOCK();
return rc;
}
/* free disk blocks from the working disk map and
* free indirect block pages.
*/
ctrunc1(ip,0);
/* delete vm segment.
*/
isegdel(ip);
/* reestablish state
*/
clrjmpx(&jbuf);
}
/* free any incore security stuff */
sec_free(ip);
/* update disk inode usage quota to reflect freed inode.
*/
freeiq(ip,1);
/* free the disk inode from the working inode map.
*/
inofree(ip,ip->i_number);
ICACHE_LOCK();
return 0;
}
/* abandon updates if mapped deferred update
*/
if (ip->i_flag & IDEFER && ip->i_seg)
{
ICACHE_UNLOCK();
isegdel(ip);
ICACHE_LOCK();
return(0);
}
/* write out compressed modified files
*/
if (ip->i_compress && vmker.noflush == 0 && ip->i_seg)
{
ICACHE_UNLOCK();
if ((ip->i_flag & IUPD) || ismodified(ip))
iflush(ip);
ICACHE_LOCK();
}
return 0;
}
/*
* NAME: iunhash (ip)
*
* FUNCTION: remove inode from inode cache
*
* PARAMETERS: ip - inode to blast
*
* RETURN : 0 - if no sleeps
* 1 - if sleeps
*
* SERIALIZATION: ICACHE_LOCK() held on entry/exit.
*/
iunhash (ip)
struct inode *ip;
{
int rc;
struct gnode *gp;
struct vnode *vp;
/* free vnode
*/
vput(ip);
/* remove from hash list
*/
REMOVE_HASH(ip);
/* free dquots.
*/
rc = putinoquota(ip);
/* free locks
*/
if (ip->i_locks & ILOCKALLOC)
{
#ifdef _I_MULT_RDRS
lock_free(&ip->i_rdwrlock);
lock_free(&ip->i_nodelock);
#else /* simple lock */
lock_free(&ip->i_rdwrlock);
#endif /* _I_MULT_RDRS */
}
if (ip->i_locks & GLOCKALLOC)
{
gp = ITOGP(ip);
lock_free(&gp->gn_reclk_lock);
}
if (ip->i_locks & VLOCKALLOC)
{
vp = ip->i_gnode.gn_vnode;
lock_free(&vp->v_lock);
}
/* insert on free list
*/
ip->i_forw = ip->i_back = NULL;
ip->i_dev = 0xdeadbeef;
ip->i_number = 0;
ip->i_mode = 0;
ip->i_count = 0;
ip->i_locks = 0;
ip->i_flag = 0;
ip->i_seg = 0;
ip->i_movedfrag = 0;
ip->i_compress = 0;
ip->i_cluster = 0;
genfree(ino_ap, ip->i_gnode.gn_vnode);
return rc;
}
/*
* NAME: i_sync()
*
* FUNCTION: commits all regular files which have not been committed
* since the last time jfs_sync() was invoked.
*
* SERIALIZATION: guarantee to not to interfere by deferring the inode
* with work in progress
*/
Simple_lock jfs_sync_lock;
void
i_sync()
{
struct hinode *hip;
struct inode *ip, *ipnext;
struct gnode *gp;
struct inode *isyncip();
SYNC_LOCK();
/* visit each inode hash list and process the inodes.
*/
for (hip = hinode; hip < &hinode[nhino]; hip++)
{
/* secure the first inode
*/
ICACHE_LOCK();
ip = isyncip(hip,hip);
ICACHE_UNLOCK();
while (ip != (struct inode *)hip)
{
if (IWRITE_LOCK_TRY(ip))
{
/* commit it if it is modified
*/
if (ip->i_seg &&
(ip->i_flag & IUPD ||
(ITOGP(ip)->gn_flags & GNF_WMAP &&
ismodified(ip))))
ip->i_flag |= IFSYNC;
if (ip->i_flag & (ICHG|IACC|IFSYNC))
commit(1,ip);
IWRITE_UNLOCK(ip);
}
ICACHE_LOCK();
/* secure the next inode */
ipnext = isyncip(ip, hip);
/* release the current inode */
iput(ip, NULL);
ICACHE_UNLOCK();
ip = ipnext;
}
}
SYNC_UNLOCK();
}
/*
* isyncip()
*
* return inode to sync
*
* SERIALIZATION: called only by isync()
* SYNC_LOCK()/ICACHE_LOCK() held on entry/exit.
*/
static
struct inode *
isyncip(xip,hip)
struct inode *xip;
struct inode *hip;
{
struct inode *ip;
ip = xip->i_forw;
while (ip != hip)
{
if (!(ip->i_locks & IXLOCK) &&
ip->i_mode != 0 &&
ip->i_nlink != 0 &&
!ISPECIAL(ip) &&
!(ip->i_flag & IDEFER))
{
/* guarantee to stay put in hashlist
* (and stay out of cachelist not to be recycled)
*/
ip->i_count++;
if (ip->i_count == 1)
{
cachesize--;
REMOVE_CACHE(ip);
NULL_CACHE(ip);
}
break;
}
ip = ip->i_forw;
}
return(ip);
}
/*
* NAME: ilogsync()
*
* FUNCTION: initiates i/o for all modified journalled pages which
* can be written to their home address or marks those
* which can not so that they will be written when they
* are committed.
*
* if iplog != NULL, sync only those inodes associated with log.
* if iplog == NULL, sync all inodes.
*
* return value - none
*
*/
ilogsync(iplog)
struct inode *iplog;
{
struct hinode *hip;
struct inode *ip, *ipnext;
struct inode *ilogsyncip();
/* visit each inode hash list and process the inodes.
*/
for (hip = hinode; hip < &hinode[nhino]; hip++)
{
/* secure the first inode
*/
ICACHE_LOCK();
ip = ilogsyncip(hip, hip, iplog);
ICACHE_UNLOCK();
while (ip != (struct inode *)hip)
{
ip->i_cflag &= ~DIRTY;
vcs_sync(ip, NULL);
ICACHE_LOCK();
/* secure the next inode */
ipnext = ilogsyncip(ip, hip, iplog);
/* release the current inode */
iput(ip, NULL);
ICACHE_UNLOCK();
ip = ipnext;
}
}
/* determine if we have to advance the
* sync point for all the logs or just
* a particular log
*/
if (iplog)
vcs_sync(NULL, iplog);
else
vcs_sync(NULL, NULL);
return 0;
}
/*
* ilogsyncip()
*
* return inode to logsync
*
* acquire reference of the inode to log sync so that getip()
* will yield the inode to log sync
*/
static
struct inode *
ilogsyncip(xip, hip, iplog)
struct inode *xip;
struct inode *hip;
struct inode *iplog;
{
struct inode *ip;
ip = xip->i_forw;
while (ip != hip)
{
/* do we sync all inodes? or
* just those associated with this log?
*/
if (iplog != NULL)
{
if (iplog != ip->i_ipmnt->i_iplog)
goto next;
}
/* before we sync the inode, make a few checks:
* 1. is this a journalled inode?
* 2. is this inode marked dirty?
* 3. is this inode currently being closed from an iput()
* through iclose().
* 4. is this inode currently being iuncached and iunhashed
* in iactivity().
*/
if ((ip->i_mode & IFJOURNAL) &&
(ip->i_cflag & DIRTY) &&
!(ip->i_cflag & ICLOSE) &&
!(ip->i_ipmnt->i_cflag & IACTIVITY))
{
/* guarantee to stay put in hashlist
* (and stay out of cachelist not to be recycled)
*/
ip->i_count++;
if (ip->i_count == 1)
{
cachesize--;
REMOVE_CACHE(ip);
NULL_CACHE(ip);
}
break;
}
next:
ip = ip->i_forw;
}
return(ip);
}
/*
* NAME: iactivity (dev, forced)
*
* FUNCTION: check if file system is quiescent to unmount, and
* clean up inodes of file system being unmounted.
*
* PARAMETERS: dev - device to check for activity
* forced - forced unmount
*
* RETURN : 0 - success
* EBUSY - busy
*
* SERIALIZATION: guarantee to remove the inode from inode cache
* by waiting on the inode if work in progress
*
* higher layer serializes mount/umount/path name
* translation/file handle translation (i.e., no
* race with _iget()).
*/
iactivity (dev, forced)
dev_t dev; /* device being unmounted */
int forced; /* boolean (!0=>shutting down) */
{
int rc = 0;
struct hinode *hip;
struct inode *ip, *ipnext, *ipmnt;
int iactivityip();
SYNC_LOCK();
ICACHE_LOCK();
/* mark the mount inode as unmounting. this
* flag is checked in ilogsyncip() and all inodes,
* in the file system which is being unmounted, are
* skipped.
*/
ipmnt = ifind(dev);
ipmnt->i_cflag |= IACTIVITY;
/* visit each inode hash list and process the inodes.
*/
for (hip = hinode; hip < &hinode[nhino]; hip++)
{
/* secure the first inode of hashlist
*/
ipnext = (struct inode *)hip;
if (rc = iactivityip(&ipnext, hip, dev, forced))
{
ipmnt->i_cflag &= ~IACTIVITY;
ICACHE_UNLOCK();
SYNC_UNLOCK();
return rc;
}
ip = ipnext;
while (ip != (struct inode *)hip)
{
/* secure the next inode */
if (rc = iactivityip(&ipnext, hip, dev, forced))
{
ipmnt->i_cflag &= ~IACTIVITY;
ICACHE_UNLOCK();
SYNC_UNLOCK();
return rc;
}
/* close the current inode.
*/
if (ip->i_mode)
iuncache(ip);
iunhash(ip);
ip = ipnext;
}
}
ICACHE_UNLOCK();
SYNC_UNLOCK();
return 0;
}
/*
* iactivityip()
*
* return inode to free
*/
static
iactivityip(xip, hip, dev, forced)
struct inode **xip;
struct inode *hip;
dev_t dev;
int forced;
{
struct inode *ip = *xip;
struct inode *iplast;
/* save the inode we are currently holding
*/
iplast = ip;
loop:
for (ip = ip->i_forw; ip != hip; ip = ip->i_forw)
{
if (ip->i_dev != dev)
continue;
/* meta-file inodes acquire single/only reference
* at mount time, and remains active to be released
* at end of umount time.
*/
if (ISPECIAL(ip) && ip->i_count == 1)
continue;
/* root inode of quiescent fs should have a single reference
* by the vfs to be released at end of umount time.
*/
if (ip->i_number == ROOTDIR_I &&
ip->i_count == 1)
continue;
/* increment the count on the inode to
* show we have interest in it. thus
* guaranteeing the inode will remain on
* the hash list
*/
ip->i_count++;
/* wait for completion of deactivation (iuncache()/iclose())
*/
if (ip->i_locks & IXLOCK)
{
do
{
ip->i_locks |= IXWANT;
e_sleep_thread(&ip->i_event, &jfs_icache_lock,
LOCK_SIMPLE);
if (ip->i_mode == 0)
{
iput(ip, NULL);
ip = iplast;
goto loop;
}
}
while (ip->i_locks & IXLOCK);
}
if (ip->i_count > 1 && !forced)
{
/* check if quota file inode.
*/
if (dqactivity(ip))
{
iput(ip, NULL);
continue;
}
iput(ip, NULL);
return EBUSY;
}
/* remove from cachelist
*/
if (ip->i_count == 1)
{
cachesize--;
REMOVE_CACHE(ip);
NULL_CACHE(ip);
}
break;
}
*xip = ip;
return 0;
}
/*
* NAME: dev_ialloc (pip, ino, mode, vfsp, ipp)
*
* FUNCTION: Allocate new in-memory+on-disk inode
*
* PARAMETERS: pip - parent inode
* ino - parent inode number
* mode - rwx and IFMT mode
* vfsp - vfs pointer
* ipp - Returned inode
*
* RETURN: If the routine fails an error number(errno) is
* returned indicating failure.
*
* NOTE: The inode allocated is locked upon return.
*/
dev_ialloc (pip, ino, mode, vfsp, ipp)
struct inode *pip; /* parent inode */
ino_t ino; /* Locate near this inode number */
mode_t mode; /* File mode */
struct vfs *vfsp; /* vfs pointer */
struct inode **ipp; /* inode to return */
{
int rc; /* Return code */
struct inode *ip; /* varying inode */
struct gnode *gp;
struct timestruc_t t;
struct hinode *hip;
*ipp = NULL;
/* allocate on-disk inode.
*/
if (rc = ialloc(pip, &ino))
return rc;
IHASH(ino, pip->i_dev, hip);
/* allocate in-memory inode
*/
ICACHE_LOCK();
/* check to see if there were any
* vgets done on this hash chain.
* the count is incremented and decremented
* in jfs_vget(). if a vget was done on the
* hash we must call _iget() to rescan.
*/
if (hip->hi_vget != 0)
rc = _iget(pip->i_dev, ino, hip, &ip, 1, vfsp);
else
rc = _iget(pip->i_dev, ino, hip, &ip, 0, vfsp);
ICACHE_UNLOCK();
sysinfo.iget++;
cpuinfo[CPUID].iget++;
if (rc)
{
inofree(pip, ino);
return rc;
}
ASSERT(ip->i_nlink == 0 && ip->i_mode == 0);
ip->i_nlink = 1;
ip->i_mode = mode;
if ((ip->i_mode & IFMT) == IFDIR)
ip->i_mode |= IFJOURNAL;
/* set update times directly to avoid extra commit in iput()
*/
curtime(&t);
ip->i_atime_ts = t;
ip->i_mtime_ts = t;
ip->i_ctime_ts = t;
ip->i_flag = ICHG;
gp = ITOGP(ip);
switch (gp->gn_type = IFTOVT(ip->i_mode))
{
case VCHR:
case VBLK:
gp->gn_rdev = ip->i_rdev;
break;
default:
gp->gn_rdev = ip->i_dev;
break;
}
/* lock the inode to return
*/
IWRITE_LOCK(ip);
*ipp = ip;
return 0;
}
/*
* NAME: vget(ip, vfsp)
*
* FUNCTION: initialize vnode of the inode at first _iget().
*
* vfsp != NULL:
* initialize v_count = 1 and link vnode to vfs (v_vfsp = vfsp);
*
* vfsp == NULL:
* . inode does not have vnode (meta-inode);
* . inode is non-device mount object (vnode management is provided by
* iptovp()/vn_get()):
* . non-root object in dir-over-dir mount being accessed in mounted fs
* before being accessed in underlying fs
* (covered inode of root of file-over-file or dir-over-dir mount is
* always accessed as part of mount).
* initialize v_count = 0 and leave vnode unlinked (v_vfsp = NULL).
*
* SERIALIZATION: ICACHE_LOCK held on entry/exit
*/
static
vget(ip, vfsp)
struct inode *ip;
struct vfs *vfsp;
{
struct vnode *vp = ip->i_gnode.gn_vnode;
assert(vp->v_vfsp == NULL);
vp->v_count = 0;
vp->v_flag = 0;
vp->v_vfsp = vfsp;
vp->v_mvfsp = NULL;
if (vfsp == NULL)
return;
vp->v_count = 1;
vp->v_audit = NULL;
vp->v_vfsgen = 0;
if (!(ip->i_locks & VLOCKALLOC))
{
lock_alloc(&vp->v_lock,LOCK_ALLOC_PAGED,VNODE_LOCK_CLASS,
vlock_cnt++);
simple_lock_init(&vp->v_lock);
ip->i_locks |= VLOCKALLOC;
}
/* insert into vfs vnodelist */
vp->v_vfsnext = vfsp->vfs_vnodes;
vfsp->vfs_vnodes = vp;
vp->v_vfsprev = NULL;
if (vp->v_vfsnext != NULL)
vp->v_vfsnext->v_vfsprev = vp;
}
/*
* NAME: vput(ip)
*
* FUNCTION: finalize vnode of the inode at iunhash()
*
* vfsp != NULL:
* unlink vnode from vfs (v_vfsp = vfsp);
*
* vfsp == NULL:
* . inode does not have vnode (meta-inode);
* . inode is non-device mount object (vnode management is provided
* by vn_free());
* . vnode of inode not initialized yet (inode initialization failure);
* vnode is already freed
* (iuncache/iunhash sequence),
*
* SERIALIZATION: ICACHE_LOCK held on entry/exit
*/
static
vput(ip)
struct inode *ip;
{
struct vnode *vp = ip->i_gnode.gn_vnode;
struct vfs *vfsp = vp->v_vfsp;
if (vfsp == NULL)
return;
/* remove from vfs vnode list */
if (vp == vfsp->vfs_vnodes)
vfsp->vfs_vnodes = vp->v_vfsnext;
if (vp->v_vfsnext != NULL)
vp->v_vfsnext->v_vfsprev = vp->v_vfsprev;
if (vp->v_vfsprev != NULL)
vp->v_vfsprev->v_vfsnext = vp->v_vfsnext;
vp->v_vfsp = NULL;
vp->v_next = NULL;
}
/*
* NAME: iptovp (vfsp, ip, vpp)
*
* FUNCTION: get a vnode give an non-device mount inode.
* Checks for the vnode in the specified vfs.
* If not there create one and initialize the gnode,
* vnode and vfsp linkage.
*
* PARAMETERS: vfsp - virtual file system where we want this
* vnode to live
* ip - inode to create from
* vpp - return newly created vnode
*
* RETURN : ENOMEM - if no space for new vnode
* else return zero
*
* SERIALIZATION:
* IWRITE_LOCK serializes v_count of softmount fs vnodes.
* gnode vnodelist is serialized by IWRITE_LOCK AFTER
* it has been initialized by _iget()).
*/
iptovp(vfsp, ip, vpp)
struct vfs *vfsp;
struct inode *ip;
struct vnode **vpp;
{
int rc;
struct vnode *vp;
struct gnode *gnp = ITOGP(ip);
*vpp = NULL;
IWRITE_LOCK(ip);
/* scan gnode vnodelist
*/
for (vp = gnp->gn_vnode; vp; vp = vp->v_next)
if (vp->v_vfsp == vfsp)
{
vp->v_count++;
IWRITE_UNLOCK(ip);
ICACHE_LOCK();
iput(ip, NULL);
ICACHE_UNLOCK();
*vpp = vp;
return 0;
}
/* allocate a new vnode
*/
if ((rc = vn_get(vfsp, ITOGP(ip), &vp)) == 0)
{
vp->v_flag = (ip->i_number == ROOTDIR_I) ? V_ROOT : 0;
*vpp = vp;
}
IWRITE_UNLOCK(ip);
if (rc)
{
ICACHE_LOCK();
iput(ip, NULL);
ICACHE_UNLOCK();
}
return rc;
}