/*
* 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 (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#include <sys/sysmacros.h>
/*
* Directory name lookup cache.
* Based on code originally done by Robert Elz at Melbourne.
*
* Names found by directory scans are retained in a cache
* for future reference. Each hash chain is ordered by LRU
* Cache is indexed by hash value obtained from (vp, name)
* where the vp refers to the directory containing the name.
*/
/*
* We want to be able to identify files that are referenced only by the DNLC.
* When adding a reference from the DNLC, call VN_HOLD_DNLC instead of VN_HOLD,
* since multiple DNLC references should only be counted once in v_count. This
* file contains only two(2) calls to VN_HOLD, renamed VN_HOLD_CALLER in the
* hope that no one will mistakenly add a VN_HOLD to this file. (Unfortunately
* it is not possible to #undef VN_HOLD and retain VN_HOLD_CALLER. Ideally a
* Makefile rule would grep uncommented C tokens to check that VN_HOLD is
* referenced only once in this file, to define VN_HOLD_CALLER.)
*/
if ((vp)->v_count_dnlc == 0) \
(vp)->v_count_dnlc++; \
}
vn_rele_dnlc(vp); \
}
/*
* Tunable nc_hashavelen is the average length desired for this chain, from
* which the size of the nc_hash table is derived at create time.
*/
/*
* NC_MOVETOFRONT is the move-to-front threshold: if the hash lookup
* depth exceeds this value, we move the looked-up entry to the front of
* its hash chain. The idea is to make sure that the most frequently
* accessed entries are found most quickly (by keeping them near the
* front of their hash chains).
*/
/*
*
* DNLC_MAX_RELE is used to size an array on the stack when releasing
* vnodes. This array is used rather than calling VN_RELE() inline because
* all dnlc locks must be dropped by that time in order to avoid a
* possible deadlock. This deadlock occurs when the dnlc holds the last
* reference to the vnode and so the VOP_INACTIVE vector is called which
* can in turn call back into the dnlc. A global array was used but had
* many problems:
* 1) Actually doesn't have an upper bound on the array size as
* entries can be added after starting the purge.
* 2) The locking scheme causes a hang.
* 3) Caused serialisation on the global lock.
* 4) The array was often unnecessarily huge.
*
* Note the current value 8 allows up to 4 cache entries (to be purged
* from each hash chain), before having to cycle around and retry.
* This ought to be ample given that nc_hashavelen is typically very small.
*/
/*
* Hash table of name cache entries for fast lookup, dynamically
* allocated at startup.
*/
/*
* Rotors. Used to select entries on a round-robin basis.
*/
/*
* # of dnlc entries (uninitialized)
*
* the initial value was chosen as being
* a random string of bits, probably not
* normally chosen by a systems administrator
*/
/*
* The dnlc_reduce_cache() taskq queue is activated when there are
* ncsize name cache entries and if no parameter is provided, it reduces
* the size down to dnlc_nentries_low_water, which is by default one
* hundreth less (or 99%) of ncsize.
*
* If a parameter is provided to dnlc_reduce_cache(), then we reduce
* the size down based on ncsize_onepercent - where ncsize_onepercent
* is 1% of ncsize; however, we never let dnlc_reduce_cache() reduce
* the size below 3% of ncsize (ncsize_min_percent).
*/
/*
* If dnlc_nentries hits dnlc_max_nentries (twice ncsize)
* then this means the dnlc_reduce_cache() taskq is failing to
* keep up. In this case we refuse to add new entries to the dnlc
* until the taskq catches up.
*/
/*
* Tunable to define when we should just remove items from
* the end of the chain.
*/
/*
* ncstats has been deprecated, due to the integer size of the counters
* which can easily overflow in the dnlc.
* It is maintained (at some expense) for compatability.
* The preferred interface is the kstat accessible nc_stats below.
*/
{ "hits", KSTAT_DATA_UINT64 },
{ "misses", KSTAT_DATA_UINT64 },
{ "negative_cache_hits", KSTAT_DATA_UINT64 },
{ "enters", KSTAT_DATA_UINT64 },
{ "double_enters", KSTAT_DATA_UINT64 },
{ "purge_total_entries", KSTAT_DATA_UINT64 },
{ "purge_all", KSTAT_DATA_UINT64 },
{ "purge_vp", KSTAT_DATA_UINT64 },
{ "purge_vfs", KSTAT_DATA_UINT64 },
{ "purge_fs1", KSTAT_DATA_UINT64 },
{ "pick_free", KSTAT_DATA_UINT64 },
{ "pick_heuristic", KSTAT_DATA_UINT64 },
{ "pick_last", KSTAT_DATA_UINT64 },
/* directory caching stats */
{ "dir_hits", KSTAT_DATA_UINT64 },
{ "dir_misses", KSTAT_DATA_UINT64 },
{ "dir_cached_current", KSTAT_DATA_UINT64 },
{ "dir_entries_cached_current", KSTAT_DATA_UINT64 },
{ "dir_cached_total", KSTAT_DATA_UINT64 },
{ "dir_start_no_memory", KSTAT_DATA_UINT64 },
{ "dir_add_no_memory", KSTAT_DATA_UINT64 },
{ "dir_add_abort", KSTAT_DATA_UINT64 },
{ "dir_add_max", KSTAT_DATA_UINT64 },
{ "dir_remove_entry_fail", KSTAT_DATA_UINT64 },
{ "dir_remove_space_fail", KSTAT_DATA_UINT64 },
{ "dir_update_fail", KSTAT_DATA_UINT64 },
{ "dir_fini_purge", KSTAT_DATA_UINT64 },
{ "dir_reclaim_last", KSTAT_DATA_UINT64 },
{ "dir_reclaim_any", KSTAT_DATA_UINT64 },
};
/*
* Insert entry at the front of the queue
*/
{ \
}
/*
* Remove entry from hash queue
*/
{ \
}
/*
* Free an entry.
*/
{ \
}
/*
* Cached directory info.
* ======================
*/
/*
* Cached directory free space hash function.
* Needs the free space handle and the dcp to get the hash table size
* Returns the hash index.
*/
/*
* Cached directory name entry hash function.
* Uses the name and returns in the input arguments the hash and the name
* length.
*/
{ \
char Xc; \
const char *Xcp; \
}
/* special dircache_t pointer to indicate error should be returned */
/*
* The anchor directory cache pointer can contain 3 types of values,
* 1) NULL: No directory cache
* 2) DC_RET_LOW_MEM (-1): There was a directory cache that found to be
* too big or a memory shortage occurred. This value remains in the
* pointer until a dnlc_dir_start() which returns the a DNOMEM error.
* This is kludgy but efficient and only visible in this source file.
* 3) A valid cache pointer.
*/
/* Tunables */
/*
* dnlc_dir_hash_resize_shift determines when the hash tables
* get re-adjusted due to growth or shrinkage
* - currently 2 indicating that there can be at most 4
* times or at least one quarter the number of entries
* before hash table readjustment. Note that with
* dnlc_dir_hash_size_shift above set at 3 this would
* mean readjustment would occur if the average number
* of entries went above 32 or below 2
*/
/* Prototypes */
int hash);
static void dnlc_dir_reclaim(void *unused);
static void do_dnlc_reduce_cache(void *);
/*
* Initialize the directory cache.
*/
void
{
int i;
/*
* Set up the size of the dnlc (ncsize) and its low water mark.
*/
if (ncsize == -1) {
/* calculate a reasonable size for the low water */
} else {
/* don't change the user specified ncsize */
}
if (ncsize <= 0) {
doingcache = 0;
dnlc_dir_enable = 0; /* also disable directory caching */
ncsize = 0;
return;
}
/*
* Initialise the hash table.
* Compute hash size rounding to the next power of two.
*/
for (i = 0; i < nc_hashsz; i++) {
}
/*
* Initialize rotors
*/
/*
* Set up the directory caching to use kmem_cache_alloc
* for its free space entries so that we can get a callback
* when the system is short on memory, to allow us to free
* up some memory. we don't use the constructor/deconstructor
* functions.
*/
NULL, 0);
/*
* Initialise the head of the cached directory structures
*/
/*
* Initialise the reference count of the negative cache vnode to 1
* so that it never goes away (VOP_INACTIVE isn't called on it).
*/
/*
* Initialise kstats - both the old compatability raw kind and
* the more extensive named stats.
*/
sizeof (struct ncstats), KSTAT_FLAG_VIRTUAL);
if (ksp) {
}
if (ksp) {
}
}
/*
* Add a name to the directory cache.
*/
void
{
int hash;
if (!doingcache) {
"dnlc_enter_end:(%S) %d", "not caching", 0);
return;
}
/*
* Get a new dnlc entry. Assume the entry won't be in the cache
* and initialize it now
*/
return;
return;
}
/*
* Insert back into the hash chain.
*/
}
/*
* Add a name to the directory cache.
*
* This function is basically identical with
* dnlc_enter(). The difference is that when the
* desired dnlc entry is found, the vnode in the
* ncache is compared with the vnode passed in.
*
* If they are not equal then the ncache is
* updated with the passed in vnode. Otherwise
* it just frees up the newly allocated dnlc entry.
*/
void
{
int hash;
if (!doingcache) {
"dnlc_update_end:(%S) %d", "not caching", 0);
return;
}
/*
* Get a new dnlc entry and initialize it now.
* If we fail to get a new entry, call dnlc_remove() to purge
* any existing dnlc entry including negative cache (DNLC_NO_VNODE)
* entry.
* Failure to clear an existing entry could result in false dnlc
*/
return;
}
} else {
"dnlc_update_end:(%S) %d",
}
return;
}
/*
* insert the new entry, since it is not in dnlc yet
*/
}
/*
* Look up a name in the directory name cache.
*
* Return a doubly-held vnode if found: one hold so that it may
* remain in the cache for other users, the other hold so that
* the cache is not re-cycled and the identity of the vnode is
* lost before the caller can use the vnode.
*/
vnode_t *
{
if (!doingcache) {
"dnlc_lookup_end:%S %d vp %x name %s",
return (NULL);
}
depth = 1;
/*
* Move this entry to the head of its hash chain
* if it's not already close.
*/
if (depth > NC_MOVETOFRONT) {
}
/*
* Put a hold on the vnode now so its identity
* can't change before the caller has a chance to
* put a hold on it.
*/
if (vp == DNLC_NO_VNODE) {
}
"dnlc_lookup_end:%S %d vp %x name %s", "hit",
return (vp);
}
depth++;
}
return (NULL);
}
/*
* Remove an entry in the directory name cache.
*/
void
{
int hash;
if (!doingcache)
return;
/*
* Free up the entry
*/
return;
}
}
/*
* Purge the entire cache.
*/
void
{
int index;
int i;
if (!doingcache)
return;
index = 0;
if (index == DNLC_MAX_RELE)
break;
}
/* Release holds on all the vnodes now that we have no locks */
for (i = 0; i < index; i++) {
VN_RELE_DNLC(nc_rele[i]);
}
nch--; /* Do current hash chain again */
}
}
}
/*
* Purge any cache entries referencing a vnode. Exit as soon as the dnlc
* reference count goes to zero (the caller still holds a reference).
*/
void
{
int index;
if (vp->v_count_dnlc == 0) {
return;
}
if (!doingcache)
return;
index = 0;
if (index == DNLC_MAX_RELE) {
break;
}
}
}
/* Release holds on all the vnodes now that we have no locks */
while (index) {
}
if (vp->v_count_dnlc == 0) {
return;
}
nch--; /* Do current hash chain again */
}
}
}
/*
* Purge cache entries referencing a vfsp. Caller supplies a count
* of entries to purge; up to that many will be freed. A count of
* zero indicates that all such entries should be purged. Returns
* the number of entries that were purged.
*/
int
{
int n = 0;
int index;
int i;
if (!doingcache)
return (0);
index = 0;
n++;
if (index == DNLC_MAX_RELE) {
break;
}
break;
}
}
}
/* Release holds on all the vnodes now that we have no locks */
for (i = 0; i < index; i++) {
VN_RELE_DNLC(nc_rele[i]);
}
return (n);
}
nch--; /* Do current hash chain again */
}
}
return (n);
}
/*
* Purge 1 entry from the dnlc that is part of the filesystem(s)
* represented by 'vop'. The purpose of this routine is to allow
* users of the dnlc to free a vnode that is being held by the dnlc.
*
* If we find a vnode that we release which will result in
* freeing the underlying vnode (count was 1), return 1, 0
* if no appropriate vnodes found.
*
* Note, vop is not the 'right' identifier for a filesystem.
*/
int
{
if (!doingcache)
return (0);
/*
* Scan the dnlc entries looking for a likely candidate.
*/
do {
continue;
break;
}
return (1);
}
return (0);
}
/*
* Perform a reverse lookup in the DNLC. This will find the first occurrence of
* the vnode. If successful, it will return the vnode of the parent, and the
* name of the entry in the given buffer. If it cannot be found, or the buffer
* is too small, then it will return NULL. Note that this is a highly
* inefficient function, since the DNLC is constructed solely for forward
* lookups.
*/
vnode_t *
{
if (!doingcache)
return (NULL);
/*
* We ignore '..' entries since it can create
* confusion and infinite loops.
*/
/* VN_HOLD 2 of 2 in this file */
return (pvp);
}
}
}
return (NULL);
}
/*
* Utility routine to search for a cache entry. Return the
* ncache entry if found, NULL otherwise.
*/
static ncache_t *
{
return (ncp);
}
return (NULL);
}
#endif
void
{
dnlc_reduce_idle = 0;
dnlc_reduce_idle = 1;
}
}
/*
* Get a new name cache entry.
* If the dnlc_reduce_cache() taskq isn't keeping up with demand, or memory
* is short then just return NULL. If we're over ncsize then kick off a
* thread to free some in use entries down to dnlc_nentries_low_water.
* Caller must initialise all fields except namlen.
* Component names are defined to be less than MAXNAMELEN
* which includes a null.
*/
static ncache_t *
{
if (dnlc_nentries > dnlc_max_nentries) {
dnlc_max_nentries_cnt++; /* keep a statistic */
return (NULL);
}
return (NULL);
}
return (ncp);
}
/*
* Taskq routine to free up name cache entries to reduce the
* cache size to the low water mark if "reduce_percent" is not provided.
* If "reduce_percent" is provided, reduce cache size by
* (ncsize_onepercent * reduce_percent).
*/
/*ARGSUSED*/
static void
{
int cnt;
if (reduce_percent) {
/*
* Never try to reduce the current number
* of cache entries below 3% of ncsize.
*/
if (dnlc_nentries <= ncsize_min_percent) {
dnlc_reduce_idle = 1;
return;
}
if (reduce_cnt > dnlc_nentries ||
else
}
do {
/*
* Find the first non empty hash queue without locking.
* Only look at each hash queue once to avoid an infinite loop.
*/
do {
/* return if all hash queues are empty. */
dnlc_reduce_idle = 1;
return;
}
/*
* A name cache entry with a reference count
* of one is only referenced by the dnlc.
* Also negative cache entries are purged first.
*/
if (!vn_has_cached_data(vp) &&
goto found;
}
/*
* Remove from the end of the chain if the
* chain is too long
*/
if (cnt > dnlc_long_chain) {
goto found;
}
}
/* check for race and continue */
continue;
}
/*
* Remove from hash chain.
*/
} while (dnlc_nentries > low_water);
dnlc_reduce_idle = 1;
}
/*
* Directory caching routines
* ==========================
*
* See dnlc.h for details of the interfaces below.
*/
/*
* Lookup up an entry in a complete or partial directory cache.
*/
{
int hash;
int ret;
/*
* can test without lock as we are only a cache
*/
return (DNOCACHE);
}
if (!dnlc_dir_enable) {
return (DNOCACHE);
}
if (VALID_DIR_CACHE(dcp)) {
return (DFOUND);
}
}
if (dcp->dc_complete) {
} else {
}
return (ret);
} else {
return (DNOCACHE);
}
}
/*
* Start a new directory cache. An estimate of the number of
* entries is provided to as a quick check to ensure the directory
* is cacheable.
*/
{
if (!dnlc_dir_enable ||
(num_entries < dnlc_dir_min_size)) {
return (DNOCACHE);
}
if (num_entries > dnlc_dir_max_size) {
return (DTOOBIG);
}
return (DNOMEM);
}
/*
* Check if there's currently a cache.
* This probably only occurs on a race.
*/
return (DNOCACHE);
}
/*
* Allocate the dircache struct, entry and free space hash tables.
* These tables are initially just one entry but dynamically resize
* when entries and free space are added or removed.
*/
goto error;
}
KM_NOSLEEP)) == NULL) {
goto error;
}
KM_NOSLEEP)) == NULL) {
goto error;
}
/* add into head of global chain */
return (DOK);
if (dcp->dc_namehash) {
}
}
/*
* Must also kmem_free dcp->dc_freehash if more error cases are added
*/
return (DNOCACHE);
}
/*
* Add a directopry entry to a partial or complete directory cache.
*/
{
int hash;
/*
* Allocate the dcentry struct, including the variable
* size name. Note, the null terminator is not copied.
*
* We do this outside the lock to avoid possible deadlock if
* dnlc_dir_reclaim() is called as a result of memory shortage.
*/
#ifdef DEBUG
/*
* The kmem allocator generates random failures for
* KM_NOSLEEP calls (see KMEM_RANDOM_ALLOCATION_FAILURE)
* So try again before we blow away a perfectly good cache.
* This is done not to cover an error but purely for
* performance running a debug kernel.
* This random error only occurs in debug mode.
*/
goto ok;
#endif
/*
* Free a directory cache. This may be the one we are
* called with.
*/
/*
* still no memory, better delete this cache
*/
if (VALID_DIR_CACHE(dcp)) {
}
return (DNOCACHE);
}
/*
* fall through as if the 1st kmem_alloc had worked
*/
}
#ifdef DEBUG
ok:
#endif
if (VALID_DIR_CACHE(dcp)) {
/*
* If the total number of entries goes above the max
* then free this cache
*/
return (DTOOBIG);
}
dcp->dc_num_entries++;
if (dcp->dc_num_entries >=
(capacity << dnlc_dir_hash_resize_shift)) {
}
/*
* Initialise and chain in new entry
*/
/*
* Note de_namelen is a uchar_t to conserve space
* and alignment padding. The max length of any
* pathname component is defined as MAXNAMELEN
* which is 256 (including the terminating null).
* So provided this doesn't change, we don't include the null,
* we always use bcmp to compare strings, and we don't
* start storing full names, then we are ok.
* The space savings is worth it.
*/
return (DOK);
} else {
return (DNOCACHE);
}
}
/*
* Add free space to a partial or complete directory cache.
*/
{
/*
* We kmem_alloc outside the lock to avoid possible deadlock if
* dnlc_dir_reclaim() is called as a result of memory shortage.
*/
#ifdef DEBUG
/*
* The kmem allocator generates random failures for
* KM_NOSLEEP calls (see KMEM_RANDOM_ALLOCATION_FAILURE)
* So try again before we blow away a perfectly good cache.
* This random error only occurs in debug mode
*/
goto ok;
#endif
/*
* Free a directory cache. This may be the one we are
* called with.
*/
/*
* still no memory, better delete this cache
*/
if (VALID_DIR_CACHE(dcp)) {
}
return (DNOCACHE);
}
/*
* fall through as if the 1st kmem_alloc had worked
*/
}
#ifdef DEBUG
ok:
#endif
if (VALID_DIR_CACHE(dcp)) {
return (DTOOBIG);
}
dcp->dc_num_free++;
if (dcp->dc_num_free >=
(capacity << dnlc_dir_hash_resize_shift)) {
}
/*
* Initialise and chain a new entry
*/
return (DOK);
} else {
return (DNOCACHE);
}
}
/*
* Mark a directory cache as complete.
*/
void
{
if (VALID_DIR_CACHE(dcp)) {
}
}
/*
* Internal routine to delete a partial or full directory cache.
* No additional locking needed.
*/
static void
{
uint_t i;
/*
* Free up the cached name entries and hash table
*/
for (i = 0; i < nhtsize; i++) { /* for each hash bucket */
nhp->de_namelen);
}
}
/*
* Free up the free space entries and hash table
*/
for (i = 0; i < fhtsize; i++) { /* for each hash bucket */
}
}
/*
* Finally free the directory cache structure itself
*/
dcp->dc_num_free);
}
/*
* Remove a partial or complete directory cache
*/
void
{
if (!VALID_DIR_CACHE(dcp)) {
return;
}
/*
* Unchain from global list
*/
}
/*
* Remove an entry from a complete or partial directory cache.
* Return the handle if it's non null.
*/
{
int hash;
int ret;
if (!dnlc_dir_enable) {
return (DNOCACHE);
}
if (VALID_DIR_CACHE(dcp)) {
if (dcp->dc_num_entries <=
(capacity >> dnlc_dir_hash_resize_shift)) {
}
}
}
te->de_namelen);
/*
* If the total number of entries
* falls below half the minimum number
* of entries then free this cache.
*/
if (--dcp->dc_num_entries <
(dnlc_dir_min_size >> 1)) {
} else {
}
return (DFOUND);
}
}
if (dcp->dc_complete) {
} else {
}
return (ret);
} else {
return (DNOCACHE);
}
}
/*
* Remove free space of at least the given length from a complete
* or partial directory cache.
*/
{
uint_t i;
int ret;
if (!dnlc_dir_enable) {
return (DNOCACHE);
}
if (VALID_DIR_CACHE(dcp)) {
if (dcp->dc_num_free <=
(capacity >> dnlc_dir_hash_resize_shift)) {
}
}
/*
* Search for an entry of the appropriate size
* on a first fit basis.
*/
for (i = 0; i < fhtsize; i++) { /* for each hash bucket */
dcp->dc_num_free--;
tfp);
return (DFOUND);
}
}
}
if (dcp->dc_complete) {
} else {
}
return (ret);
} else {
return (DNOCACHE);
}
}
/*
* Remove free space with the given handle from a complete or partial
* directory cache.
*/
{
int ret;
if (!dnlc_dir_enable) {
return (DNOCACHE);
}
if (VALID_DIR_CACHE(dcp)) {
if (dcp->dc_num_free <=
(capacity >> dnlc_dir_hash_resize_shift)) {
}
}
/*
* search for the exact entry
*/
dcp->dc_num_free--;
return (DFOUND);
}
}
if (dcp->dc_complete) {
} else {
}
return (ret);
} else {
return (DNOCACHE);
}
}
/*
* Update the handle of an directory cache entry.
*/
{
int hash;
int ret;
if (!dnlc_dir_enable) {
return (DNOCACHE);
}
if (VALID_DIR_CACHE(dcp)) {
return (DFOUND);
}
}
if (dcp->dc_complete) {
} else {
}
return (ret);
} else {
return (DNOCACHE);
}
}
void
{
if (VALID_DIR_CACHE(dcp)) {
/*
* Unchain from global list
*/
} else {
}
if (dcp) {
}
}
/*
* Reclaim callback for dnlc directory caching.
* Invoked by the kernel memory allocator when memory gets tight.
* This is a pretty serious condition and can lead easily lead to system
* hangs if not enough space is returned.
*
* Deciding which directory (or directories) to purge is tricky.
* Purging everything is an overkill, but purging just the oldest used
* was found to lead to hangs. The largest cached directories use the
* most memory, but take the most effort to rebuild, whereas the smaller
* ones have little value and give back little space. So what to do?
*
* The current policy is to continue purging the oldest used directories
* until at least dnlc_dir_min_reclaim directory entries have been purged.
*/
/*ARGSUSED*/
static void
{
while (dirent_cnt < dnlc_dir_min_reclaim) {
} else {
}
}
}
/* nothing to delete */
return;
}
/*
* remove from directory chain and purge
*/
/*
* If this was the last entry then it must be too large.
* Mark it as such by saving a special dircache_t
* pointer (DC_RET_LOW_MEM) in the anchor. The error DNOMEM
* will be presented to the caller of dnlc_dir_start()
*/
} else {
}
}
}
/*
* Dynamically grow or shrink the size of the name hash table
*/
static void
{
int i;
/*
* Allocate new hash table
*/
/*
* System is short on memory just return
* Note, the old hash table is still usable.
* This return is unlikely to repeatedy occur, because
* either some other directory caches will be reclaimed
* due to memory shortage, thus freeing memory, or this
* directory cahe will be reclaimed.
*/
return;
}
/*
* Move entries from the old table to the new
*/
for (i = 0; i < oldsize; i++) { /* for each hash bucket */
}
}
/*
* delete old hash table and set new one in place
*/
}
/*
* Dynamically grow or shrink the size of the free space hash table
*/
static void
{
int i;
/*
* Allocate new hash table
*/
/*
* System is short on memory just return
* Note, the old hash table is still usable.
* This return is unlikely to repeatedy occur, because
* either some other directory caches will be reclaimed
* due to memory shortage, thus freeing memory, or this
* directory cahe will be reclaimed.
*/
return;
}
/*
* Move entries from the old table to the new
*/
for (i = 0; i < oldsize; i++) { /* for each hash bucket */
}
}
/*
* delete old hash table and set new one in place
*/
}