ufs_thread.c revision 709bb9d7e51d9eb1a54651e14434668097805c75
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
extern pri_t minclsyspri;
extern int hash2ints();
extern int ufs_idle_waiters;
static void ufs_attr_purge(struct inode *);
/*
* initialize a thread's queue struct
*/
void
{
}
/*
* start a thread for a queue (assumes success)
*/
void
{
}
}
/*
* wait for the thread to exit
*/
void
{
kt_did_t ufs_thread_did = 0;
}
/*
* It's safe to call thread_join() with an already-gone
* t_did, but we have to obtain it before the kernel
* thread structure is freed. We do so above under the
* protection of the uq_mutex when we're sure the thread
* still exists and it's save to de-reference it.
* We also have to check if ufs_thread_did is != 0
* before calling thread_join() since thread 0 in the system
* gets a t_did of 0.
*/
if (ufs_thread_did)
}
/*
* wait for a thread to suspend itself on the caller's behalf
* the caller is responsible for continuing the thread
*/
void
{
/*
* wait while another thread is suspending this thread.
* no need to do a cv_broadcast(), as whoever suspended
* the thread must continue it at some point.
*/
/*
* We can't use cv_signal() because if our
* signal doesn't happen to hit the desired
* thread but instead some other waiter like
* ourselves, we'll wait forever for a
* response. Well, at least an indeterminate
* amount of time until we just happen to get
* lucky from whomever did get signalled doing
* a cv_signal() of their own. This is an
* unfortunate performance lossage.
*/
}
/*
* wait for the thread to suspend itself
*/
}
}
}
}
/*
* allow a thread to continue from a ufs_thread_suspend()
* This thread must be the same as the thread that called
* ufs_thread_suspend.
*/
void
{
}
/*
* some common code for managing a threads execution
* uq is locked at entry and return
* may sleep
* may exit
*/
/*
* Kind of a hack passing in the callb_cpr_t * here.
* It should really be part of the ufs_q structure.
* I did not put it in there because we are already in beta
* and I was concerned that changing ufs_inode.h to include
* callb.h might break something.
*/
int
{
/*
* exiting; empty the queue (may infinite loop)
*/
}
thread_exit();
/*
* process a block of entries until below high water mark
*/
}
}
goto again;
}
/*
* DELETE INODE
* The following routines implement the protocol for freeing the resources
* held by an idle and deleted inode.
*/
void
{
int trans_size;
int issync;
int err;
/*
* not on a trans device or not part of a transaction
*/
/*
*/
return;
}
return;
}
/*
* If we are called as part of setting a fs lock, then only
* do part of the lockfs protocol. In other words, don't hang.
*/
if (dolockfs) {
return;
} else {
/*
* check for recursive VOP call
*/
} else {
}
}
/*
* Hold rwlock to synchronize with (nfs) writes
*/
if (dorwlock)
/*
* Delete the attribute directory.
*/
if (ip->i_oeftflag != 0) {
if (err == 0) {
/*
* Should get rid of any negative cache entries that
* might be lingering, as well as ``.'' and
* ``..''. If we don't, the VN_RELE() below
* won't actually put dp on the delete queue
* and it'll hang out until someone forces it
* (lockfs -f, umount, ...). The only reliable
* way of doing this at the moment is to call
* dnlc_purge_vp(ITOV(dp)), which is unacceptably
* slow, so we'll just note the problem in this
* comment for now.
*/
if (!TRANS_ISTRANS(ufsvfsp)) {
}
}
/*
* Clear out attribute pointer
*/
ip->i_oeftflag = 0;
}
}
/*
* the inode's space has been freed; now free the inode
*/
if (ulp) {
}
}
/*
* This inode is torn down but still retains it's identity
* (inode number). It could get recycled soon so it's best
* to clean up the vnode just in case.
*/
vn_recycle(vp);
/*
* free the inode
*/
/*
* release quota resources; can't fail
*/
if (!TRANS_ISTRANS(ufsvfsp)) {
} else {
}
if (dorwlock)
/*
* End of transaction
*/
if (ulp) {
if (dolockfs)
else
}
}
/*
* Create the delete thread and init the delq_info for this fs
*/
void
{
}
/*
* thread that frees up deleted inodes
*/
void
{
long ne;
"ufsdelete");
/*
* Sleep until there is work to do. Only do one entry at
* a time, to reduce the wait time for checking for a suspend
* request. The ?: is for pedantic portability.
*/
/*
* process an entry, if there are any
*/
/*
* process first entry on queue. Assumed conditions are:
* ip is held (v_count >= 1)
* ip is referenced (i_flag & IREF)
* ip is free (i_nlink <= 0)
*/
}
goto again;
}
/*
* drain ne entries off the delete queue. As new queue entries may
* be added while we're working, ne is interpreted as follows:
*
* ne > 0 => remove up to ne entries
* ne == 0 => remove all entries currently on the queue
* ne == -1 => remove entries until the queue is empty
*/
void
{
int drain_cnt = 0;
int done;
/*
* if forcibly unmounted; ignore
*/
return;
if (ne == 0)
else if (ne > 0)
/*
* process up to ne entries
*/
done = 0;
if (ne != -1)
drain_cnt--;
done = 1;
}
}
void
{
/*
* Wake up delete thread to free up space.
*/
}
}
}
/*
* Get rid of everything that's currently in the delete queue,
* plus whatever the delete thread is working on at the moment.
*
* This ability is required for providing true POSIX semantics
* regarding close(2), unlink(2), etc, even when logging is enabled.
* The standard requires that the released space be immediately
* observable (statvfs(2)) and allocatable (e.g., write(2)).
*/
void
{
int error;
/*
* If there is something on delq or delete thread
* working on delq.
*/
if (delq_info->delq_unreclaimed_files > 0) {
} else {
return;
}
/*
* Commit any outstanding transactions to make sure
* any canceled freed blocks are available for allocation.
*/
if (!error) {
}
}
/*
* Adjust the resource usage in a struct statvfs based on
* what's in the delete queue.
*
* We do not consider the impact of ACLs or extended attributes
* that may be deleted as a side-effect of deleting a file.
* Those are metadata, and their sizes aren't reflected in the
* sizes returned by stat(), so this is not a problem.
*/
void
{
/*
* The blocks accounted for in the delete queue info are
* counted in DEV_BSIZE chunks, but ufs_statvfs counts in
* filesystem fragments, so a conversion is required here.
*/
}
/*
* IDLE INODE
* The following routines implement the protocol for maintaining an
* LRU list of idle inodes and for moving the idle inodes to the
* reuse list when the number of allocated inodes exceeds the user
* tunable high-water mark (ufs_ninode).
*/
/*
* clean an idle inode and move it to the reuse list
*/
static void
{
int pages;
int hno;
/*
* inode is held
*/
/*
* remember `pages' for stats below
*/
/*
* start the dirty pages to disk and then invalidate them
* unless the inode is invalid (ISTALE)
*/
(void) TRANS_SYNCIP(ip,
}
/*
* wait for any current ufs_iget to finish and block future ufs_igets
*/
/*
* It must be guaranteed that v_count >= 2, otherwise
* something must be wrong with this vnode already.
* That is why we use v_count-- instead of VN_RELE().
* Acquire the vnode lock in case another thread is in
* VN_RELE().
*/
"ufs_idle_free: vnode ref count is less than 2");
/*
* Another thread has referenced this inode while
* we are trying to free it. Call VN_RELE() to
* release our reference.
*/
} else {
/*
* The inode is currently unreferenced and can not
* acquire further references because it has no pages
* and the hash is locked. Inodes acquire references
* via the hash list or via their pages.
*/
/*
* remove it from the cache
*/
/*
* Stale inodes have no valid ufsvfs
*/
}
ufs_si_del(ip);
if (pages) {
} else {
}
/*
* We had better not have a vnode reference count > 1
* at this point, if we do then something is broken as
*/
}
}
/*
* this thread processes the global idle queue
*/
int ufs_njunk_iq = 0;
int ufs_nuseful_iq = 0;
int ufs_niqhash;
int ufs_iqhashmask;
struct ufs_q ufs_idle_q;
void
ufs_thread_idle(void)
{
int i;
int ne;
KM_SLEEP);
KM_SLEEP);
/* Initialize hash queue headers */
for (i = 0; i < ufs_niqhash; i++) {
}
"ufsidle");
/*
* Whenever the idle thread is awakened, it repeatedly gives
* back half of the idle queue until the idle queue falls
* below lowat.
*/
}
/*
* Give back 1/2 of the idle queue
*/
goto again;
}
/*
* Reclaim callback for ufs inode cache.
* Invoked by the kernel memory allocator when memory gets tight.
*/
/*ARGSUSED*/
void
ufs_inode_cache_reclaim(void *cdrarg)
{
/*
* If we are low on memory and the idle queue is over its
* halfway mark, then free 50% of the idle q
*
* We don't free all of the idle inodes because the inodes
* for popular NFS files may have been kicked from the dnlc.
* The inodes for these files will end up on the idle queue
* after every NFS access.
*
* If we repeatedly push them from the idle queue then
* NFS users may be unhappy as an extra buf cache operation
* is incurred for every NFS operation to these files.
*
* It's not common, but I have seen it happen.
*
*/
return;
}
/*
* Free up some idle inodes
*/
void
ufs_idle_some(int ne)
{
int i;
static int junk_rotor = 0;
static int useful_rotor = 0;
for (i = 0; i < ne; ++i) {
if (ufs_njunk_iq) {
}
} else if (ufs_nuseful_iq) {
}
} else {
return;
}
/*
* emulate ufs_iget
*/
/*
* VN_RELE should not be called if
* ufs_rmidle returns true, as it will
* effectively be done in ufs_idle_free.
*/
if (ufs_rmidle(ip)) {
} else {
}
}
}
/*
* drain entries for vfsp from the idle queue
* vfsp == NULL means drain the entire thing
*/
void
{
int i;
if (ufs_njunk_iq) {
/* for each hash q */
for (i = 0; i < ufs_niqhash; i++) {
/* search down the hash q */
/* found a matching entry */
/*
* See comments in ufs_idle_some()
* as we will call ufs_idle_free()
* after scanning both queues.
*/
if (ufs_rmidle(ip)) {
} else {
}
/* restart this hash q */
}
}
}
}
if (ufs_nuseful_iq) {
/* for each hash q */
for (i = 0; i < ufs_niqhash; i++) {
/* search down the hash q */
/* found a matching entry */
/*
* See comments in ufs_idle_some()
* as we will call ufs_idle_free()
* after scanning both queues.
*/
if (ufs_rmidle(ip)) {
} else {
}
/* restart this hash q */
}
}
}
}
/* no more matching entries, release those we have found (if any) */
}
}
/*
* RECLAIM DELETED INODES
* The following thread scans the file system once looking for deleted files
*/
void
{
int err = 0;
"ufsreclaim");
/*
* mount decided that we don't need a reclaim thread
*/
err++;
/*
* don't reclaim if readonly
*/
err++;
/*
* Check whether we are the target of another
* thread having called ufs_thread_exit() or
* ufs_thread_suspend().
*/
err++;
break;
/*
* Release the buf before we cv_wait()
* otherwise we may deadlock with the
* thread that called ufs_thread_suspend().
*/
if (bp) {
bp = 0;
}
}
goto again;
}
/*
* if we don't already have the buf; get it
*/
if (bp)
}
err++;
continue;
}
/*
* nlink <= 0 and mode != 0 means deleted
*/
/*
* can't hold the buf (deadlock)
*/
bp = 0;
/*
* thread queue if it is idle. This is a nop
* for busy (open, deleted) inodes
*/
err++;
else
}
}
if (bp)
if (!err) {
/*
* reset the reclaiming-bit
*/
}
/*
* exit the reclaim thread
*/
thread_exit();
}
/*
* HLOCK FILE SYSTEM
* hlock the file system's whose logs have device errors
*/
/*ARGSUSED*/
void
ufs_thread_hlock(void *ignore)
{
int retry;
"ufshlock");
for (;;) {
/*
* sleep until there is work to do
*/
/*
* hlock the error'ed fs's
* retry after a bit if another app is doing lockfs stuff
*/
do {
retry = ufs_trans_hlock();
if (retry) {
}
} while (retry);
}
}
static void
{
int err;
int error;
int entryoffsetinblk; /* offset of ep in fbp's buffer */
int trans_size;
int issync;
offset = 0;
entryoffsetinblk = 0;
/*
* Purge directory cache
*/
/*
* If offset is on a block boundary,
* read the next directory block.
* Release previous if it exists.
*/
}
if (err) {
goto out;
}
entryoffsetinblk = 0;
}
} else {
goto out;
}
/*
* Delete inode.
*/
}
}
if (fbp) {
}
out:
}