cachefs_cnode.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/pathname.h>
#include <sys/sysmacros.h>
/*
* cachefs_max_idle is a global that is tunable.
* This value decides how frequently or when the
* cachefs_cnode_idleclean is run.
* The default value is set to CFS_FS_MAXIDLE.
* The tunable if set to X triggers a cleanup when
* the number of idle cnodes reach X, and cleans up
* (.25 * X) idle cnodes.
*/
int cachefs_max_idle = CFS_FS_MAXIDLE;
/*
* Functions for cnode management.
*/
/*
* Puts cnode on idle list. Only call from an async thread or no
* locks held.
*/
/*ARGSUSED1*/
void
{
int cleanidle;
char *unlname;
int error;
/*
* The key to this routine is not to drop the vnode count
* while on the idle list. This prevents this routine from
* being called again by vn_rele on an inactive cnode.
* Nothing bad happens if an "active" cnode is put on the idle
* list. It eventually gets pulled off.
* Also this routine is only called from a thread message sent
* by cachefs_inactive(). It is not safe for this routine
* to be the "inactive" entry point because of the dnlc.
*/
for (;;) {
/* get access to the file system */
/* get exclusive access to this cnode */
/* done with this loop if not unlinking a file */
break;
/* get unlink info out of the cnode */
/* finish the remove operation */
} else {
}
/* reacquire cnode lock */
/* if a timeout occurred */
/* restore cnode state */
continue;
} else {
goto out;
}
}
}
/* free up resources */
break;
}
/*
* If we are going to destroy this cnode,
* do it now instead of later.
*/
goto out;
}
/*
* mark cnode as idle, put it on the idle list, and increment the
* number of idle cnodes
*/
(fscp->fs_idleclean == 0) &&
(fscp->fs_cdtransition == 0)) {
cleanidle = 1;
} else {
cleanidle = 0;
}
/* release cnode */
/* if should reduce the number of idle cnodes */
if (cleanidle) {
/* XXX race with cachefs_unmount() calling destroy */
}
out:
/* release hold on the file system */
/* XXX unmount() could have called destroy after fscache_rele() */
}
/*
* Removes cnodes from the idle list and destroys them.
*/
void
{
int remcnt;
/* determine number of cnodes to destroy */
if (unmount) {
/* destroy all plus any that go idle while in this routine */
} else {
/* reduce to 75% of max allowed idle cnodes */
(cachefs_max_idle >> 2);
}
/* get cnode on back of idle list and hold it */
break;
/* if the cnode is still on the idle list */
/* remove cnode from the idle list */
/* destroy the cnode */
} else {
/* cnode went active, just skip it */
}
}
fscp->fs_idleclean = 0;
}
/*
* This routine does the real work of inactivating a cachefs vnode.
*/
int
{
struct cachefs_metadata *mdp;
int meta_destroyed = 0;
/* truncate the front file if necessary */
#ifdef CFSDEBUG
printf("c_cnode_inactive: invalidating %llu\n",
#endif
/*
* If the cnode is being populated, and we're not the
* populating thread, then block until the pop thread
* completes. If we are the pop thread, then we may come in
* here, but not to nuke the directory cnode at a critical
* juncture.
*/
}
for (;;) {
/* see if vnode is really inactive */
/*
* It's impossible for us to be cnode_inactive for
* the root cnode _unless_ we are being called from
* cachefs_unmount (where inactive is called
* explictly). If the count is not 1, there is
* still an outstanding reference to the root cnode,
* and we return EBUSY; this allows cachefs_unmount
* to fail.
*/
return (EBUSY);
}
cp->c_ipending = 0;
return (0);
}
/* get rid of any pages, do not care if cannot be pushed */
if (vn_has_cached_data(vp)) {
}
/* if need to sync metadata, the call is a no op for NFSv4 */
(void) cachefs_sync_metadata(cp);
continue;
}
break;
}
/*
* Lock out possible race with makecachefsnode.
* be correct when it gets to run.
*/
/* see if vnode is still inactive */
cp->c_ipending = 0;
#ifdef CFSDEBUG
printf("cachefs_cnode_inactive: %u vp %p\n",
#endif
return (0);
}
/* check for race with remove */
/* this causes cachefs_inactive to be called again */
return (0);
}
/* if any pages left, really get rid of them */
if (vn_has_cached_data(vp)) {
}
/* if we can (and should) destroy the front file and metadata */
}
meta_destroyed = 1;
}
}
/* else put the front file on the gc list */
#ifdef CFSDEBUG
#endif /* CFSDEBUG */
mdp->md_frontblks);
}
/* if idlelist pointer(s) not null, remove from idle list */
}
/* remove from the filegrp list prior to releasing the cnode lock */
if (! meta_destroyed)
(void) cachefs_sync_metadata(cp);
}
if (cp->c_acldirvp)
/* free up cnode memory */
return (0);
}
/*
* Add a cnode to the filegrp list.
*/
void
{
}
/*
* Remove a cnode from the filegrp list.
*/
void
{
#ifdef CFSDEBUG
int found = 0;
#endif
#ifdef CFSDEBUG
found++;
#endif
break;
}
}
#ifdef CFSDEBUG
#endif
}
/*
* Add a cnode to the front of the fscache idle list.
*/
void
{
/* put cnode on the front of the idle list */
if (fscp->fs_idlefront)
else {
}
fscp->fs_idlecnt++;
}
/*
* Remove a cnode from the fscache idle list.
*/
void
{
} else {
}
} else {
}
fscp->fs_idlecnt--;
}
/*
* Search the cnode list of the input file group, looking for a cnode which
* matches the supplied file ident fileno.
*
* Returns:
* *cpp = NULL, if no valid matching cnode is found
* *cpp = address of cnode with matching fileno, with c_statelock held
* return status is 0 if no cnode found, or if found & cookies match
* return status is 1 if a cnode was found, but the cookies don't match
*
* Note: must grab the c_statelock for each cnode, or its state could
* change while we're processing it. Also, if a cnode is found, must return
* with c_statelock still held, so that the cnode state cannot change until
* the calling routine releases the lock.
*/
int
{
int badcookie = 0;
#ifdef CFSDEBUG
#endif
/*
* Cookie should be filled unless disconnected operation or
* backfilesystem is NFSv4
*/
goto out;
}
continue;
}
/*
* Having found a non stale, non destroy pending cnode with
* matching fileno, will be exiting the for loop, after
* determining return status
*/
#ifdef CFSDEBUG
"cachefs: dup fileno %llu, cp %p\n",
}
#endif
badcookie = 1;
}
/*
* For NFSv4 since there is no fid, add a check to
* ensure the backvp and vap matches that in the cnode.
* If it doesn't then someone tried to use a stale cnode.
*/
if (is_nfsv4) {
("cachefs_cnode_find (nfsv4): stale cnode "
"cnode %p, backvp %p, new-backvp %p, vap %p "
"fileno=%llx cp-fileno=%llx\n",
badcookie = 1;
}
}
break;
}
out:
#ifdef CFSDEBUG
#endif
return (badcookie);
}
/*
* We have to initialize the cnode contents. Fill in the contents from the
* cache (attrcache file), from the info passed in, whatever it takes.
*/
static int
{
int error = 0;
int slotfound;
int null_cookie;
}
if (cookiep)
/*
* if nocache is set then ignore anything cached for this file,
* if nfsv4 flag is set, then create the cnode but don't do
* any caching.
*/
/*
* this case only happens while booting without a cache
* or if NFSv4 is the backfilesystem
*/
if (error)
goto out;
} else
goto out;
}
/*
* if not, and there's no cookie info, nothing can be done, but if
* there's cookie data indicate we need to create a metadata slot.
*/
if (slotfound == 0) {
goto out;
}
} else {
/*
* if a slot was found, then increment the slot in use count
* and try to read the metadata.
*/
&cp->c_metadata);
}
/*
* if there wasn't a slot, or an attempt to read it results in ENOENT,
* then init the cache object, create the vnode, etc...
*/
if (error)
goto out;
} else if (error == 0) {
/* slot found, no error occurred on the metadata read */
if (error) {
goto out;
}
}
}
/*
* If no cookie is specified, or if this is a local file,
* accept the one in the metadata.
*/
null_cookie = 0;
null_cookie = 1;
}
/* if cookies do not match, reset the metadata */
/* clear all but the front file bit */
}
/* else if the consistency type changed, fix it up */
if (!null_cookie) {
C_BACK_CHECK, cr);
}
}
/* else check the consistency of the data */
else {
if (!null_cookie) {
}
}
} else {
goto out;
}
out:
if (error) {
if (cp->c_acldirvp)
}
return (error);
}
/*
* Finds the cnode for the specified fileno and fid.
* Creates the cnode if it does not exist.
* The cnode is returned held.
*/
int
{
int error;
struct cachefs_metadata *mdp;
#ifdef CFSDEBUG
printf("cachefs_cnode_make: ENTER fileno %llu\n",
#endif
/* get the file group that owns this file */
}
/* grab the cnode list lock */
flag |= CN_NOCACHE;
error = 0;
/* look for the cnode on the cnode list */
/*
* If there already is a cnode with this cid but a different cookie,
* (or backvp) we're not going to be using the one we found.
*/
error = 0;
} else if (error) {
/*
* If backvp is NULL then someone tried to use
* a stale cookie.
*/
goto out;
}
/* verify the backvp */
if (error ||
goto out;
}
/* make the old cnode give up its front file resources */
(void) cachefs_sync_metadata(cp);
/* XXX sam: should this assert be NOCACHE? */
/* XXX sam: maybe we should handle NOFILL as no-op */
/* if modified in the cache, move to lost+found */
if (error) {
goto out;
}
}
/* else nuke the front file */
else {
}
} else {
}
error = 0;
}
/* if the cnode does not exist */
/* XXX should we drop all locks for this? */
if (error) {
goto out;
}
#ifdef CFSDEBUG
#endif /* CFSDEBUG */
}
}
/* else if the cnode exists */
else {
/* remove from idle list if on it */
cp->c_ipending = 0;
}
}
/*
* Assertion to ensure the cnode matches
* the backvp and attribute type information.
*/
out:
#ifdef CFSDEBUG
printf("cachefs_cnode_make: EXIT cp %p, error %d\n",
#endif
return (error);
}
/*
* cachefs_cid_inuse()
*
* returns nonzero if a cid has any data in the cache; either a cnode
* or metadata.
*/
int
{
int status = 0;
/*
* Since we don't care about the cookie data, we don't care about any
* status that find might return.
*/
status = 1;
return (status);
}
/*
* Don't want to use filegrp_read_metadata, since it will return
* ENOENT if the metadata slot exists but hasn't been written to yet.
* That condition still counts as the slot (metadata) being in use.
* Instead, as long as the filegrp attrcache has been created and
* there's a slot assigned for this cid, then the metadata is in use.
*/
status = 1;
return (status);
}
/*
* cachefs_fileno_inuse()
*
* returns nonzero if a fileno is known to the cache, as either a
* local or a normal file.
*/
int
{
int known = 0;
/* if there's no filegrp for this cid range, then there's no data */
return (known);
known = 1;
goto out;
}
known = 1;
out:
return (known);
}
/*
* Creates a cnode from an unused inode in the cache.
* The cnode is returned held.
*/
int
{
/* find an unused local file in the cache */
for (;;) {
/* make sure we did not wrap */
/* avoid fileno conflict in non-local space */
if (found) {
continue;
}
}
/* get the file group that owns this fileno */
}
/* see if there is any room left in this file group */
/* no more room, set up for the next file group */
continue;
}
(CFS_FG_READ | CFS_FG_WRITE)) ==
(CFS_FG_READ | CFS_FG_WRITE));
/* grab the cnode list lock */
flag |= CN_NOCACHE;
/* keep looking if a cnode or metadata exist for this fileno */
#ifdef CFSDEBUG
"fileno %llu exists.\n",
#endif
continue;
}
break;
}
/* create space for the cnode */
/* set up the cnode */
if (error) {
goto out;
}
/* save copy of fileno that is returned to the user */
out:
return (error);
}
/*
* Moves the cnode to its new location in the cache.
* Before calling this routine other steps must be taken
* to ensure that other file system routines that operate
* on cnodes do not run.
*/
void
{
struct cachefs_metadata *mdp;
char oname[CFS_FRONTFILE_NAME_SIZE];
char nname[CFS_FRONTFILE_NAME_SIZE];
int ffnuke = 0;
int error;
/* construct the cid of the new file location */
/* see if there already is a file occupying our slot */
0, &xcp);
if (error == 0) {
error = 0;
}
/* get the file group that this file is moving to */
}
/* XXX fix to not have to create metadata to hold rl slot */
/* get a metadata slot in the new file group */
(void) filegrp_allocattr(fgp);
}
/* XXX can fix create_metadata to call allocattr if necessary? */
if (error)
ffnuke = 1;
ffnuke = 1;
/* move the front file to the new file group */
if (error) {
ffnuke = 1;
#ifdef CFSDEBUG
printf("cachefs: cnode_move "
"1: error %d\n", error);
}
#endif
}
}
/* remove the file from the old file group */
}
if (cp->c_acldirvp) {
}
if (ffnuke) {
} else {
}
}
if (ffnuke)
}
/* add the cnode to the new file group */
}
/*
* Syncs out the specified cnode.
* Only called via cnode_traverse from fscache_sync
*/
void
{
int error = 0;
int held = 0;
return;
return;
for (;;) {
/* get (or renew) access to the file system */
if (held) {
held = 0;
}
/*
* Getting file system access for reading is really cheating.
* However we are getting called from sync so we do not
* want to hang up if the cachefsd is not running.
*/
if (error)
break;
held = 1;
/* if a regular file, write out the pages */
0, 0, kcred);
held = 0;
continue;
} else {
/* cannot push, give up */
break;
}
}
/* clear the cnode error if putpage worked */
}
if (error)
break;
}
/* if connected, sync the backvp */
NULL);
held = 0;
continue;
}
}
/* sync the metadata and the front file to the front fs */
(void) cachefs_sync_metadata(cp);
break;
}
if (held)
}
/*
* Moves the specified file to the lost+found directory for the
* cached file system.
* Invalidates cached data and attributes.
* Returns 0 or an error if could not perform operation.
*/
int
{
int error = 0;
char oname[CFS_FRONTFILE_NAME_SIZE];
int index;
int len;
/* set up the file group if necessary */
if (error)
goto out;
}
#ifdef CFSDEBUG
printf("cachefs_cnode_lostfound cp %p cannot save\n",
(void *)cp);
#endif
goto out;
}
/* lock out other users of the lost+found directory */
/* find a name we can use in lost+found */
if (rname)
else
namep = "lostfile";
if (error == 0)
#define MAXTRIES 1000
if (len > MAXNAMELEN)
else
if (error == 0)
break;
}
goto out;
}
}
/* get the name of the front file */
/* rename the file into the lost+found directory */
if (error) {
goto out;
}
/* copy out the new name */
if (rname)
out:
/* clean up */
if (namebuf)
#if 0 /* XXX until we can put filesystem in read-only mode */
if (error) {
/* XXX put file system in read-only mode */
}
#endif
return (error);
}
/*
* Traverses the list of cnodes on the fscache and calls the
* specified routine with the held cnode.
*/
void
{
int index;
/* lock the fscache while we traverse the file groups */
/* for each bucket of file groups */
/* for each file group in a bucket */
/* hold the file group */
/* drop fscache lock so others can use it */
/* drop hold on previous file group */
if (ofgp)
/* lock the cnode list while we traverse it */
/* for each cnode in this file group */
/* hold the cnode */
/* drop cnode list lock so others can use it */
/* drop hold on previous cnode */
if (ocp) {
}
/*
* Execute routine for this cnode.
* At this point no locks are held.
*/
/* reacquire the cnode list lock */
}
/* drop cnode list lock */
/* drop hold on last cnode */
if (ocp) {
}
/* reacquire the fscache lock */
}
/* drop hold on last file group */
if (ofgp)
}
}
void
{
}
}
static void
{
struct cachefs_metadata md;
int error;
if (iovp) {
}
}
if (error == 0) {
if (error) {
goto out;
}
}
}
/*
* A rudimentary consistency check
* here. If the cookie and mtime
* from the cnode match those from the
* cache metadata, we assume for now that
* the cached data is OK.
*/
} else {
/*
* Here we're skeptical about the validity of
* the front file.
* We'll keep the attributes already present in
* the cnode, and bring along the parts of the
* metadata that we need to eventually nuke this
* bogus front file -- in inactive or getfrontfile,
* whichever comes first...
*/
}
sizeof (struct cachefs_allocmap));
}
#ifdef CFSDEBUG
"fileno %lld ignores cached data due "
}
#endif
}
}
}
out:
}
void
{
/*
* This function is only called when a remount occurs,
* with "nocache" and "nofill" options configured
* (currently these aren't supported). Since this
* function can write into the cache, make sure that
* its not in use with NFSv4.
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp))
return;
/*
* set up file groups so we can read them. Note that general
* users (makecfsnode) will *not* start using them (i.e., all
* newly created cnodes will be NOCACHE)
* until we "enable_caching_rw" below.
*/
/* enable general use of the filegrps */
}
/*
* This function makes a cnode stale by performing the following tasks:
* 1) remove the front file
* 2) Remove any resource file entries
* 3) Remove any metadata entry from the attrcache file
* 4) Set the stale bit in the cnode flags field
*/
void
{
struct cachefs_metadata *mdp;
/*
* Remove a metadata entry if the file exists
*/
/*
* destroy the frontfile
*/
/*
* Remove resource file entry
*/
}
/*
* Remove attrcache metadata
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp) == 0)
}
/*
* For NFSv4 need to hang on to the backvp until vn_rele()
* frees this cnode.
*/
}
if (cp->c_acldirvp) {
}
}
/*
* Sets up the local attributes in the metadata from the attributes.
*/
void
{
/* allow over writing of local attributes if a remount occurred */
}
}
/* overwrite old fileno and timestamps if not local versions */
}