cachefs_vfsops.c revision aa59c4cb15a6ac5d4e585dadf7a055b580abf579
/*
* 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/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/sysmacros.h>
#include <sys/bootconf.h>
extern kmutex_t cachefs_kmem_lock;
/* forward declarations */
static void cachefs_delete_cachep(cachefscache_t *);
#define CFS_MAPSIZE 256
int cachefs_mount_retries = 3;
major_t cachefs_major = 0;
minor_t cachefs_minor = 0;
int cachefs_kstat_key_n = 0;
/*
* cachefs vfs operations.
*/
/*
* Initialize the vfs structure
*/
int cachefsfstyp;
int cnodesize = 0;
int
{
static const fs_operation_def_t cachefs_vfsops_template[] = {
};
int error;
if (error != 0)
return (error);
return (0);
}
cachefs_mkmntdev(void)
{
do {
} while (vfs_devismounted(cachefs_dev));
return (cachefs_dev);
}
/*
* vfs operations
*/
static int
{
struct cachefsoptions *cfs_options;
int error = 0;
int retries = cachefs_mount_retries;
char *backmntpt;
char tmpstr[MAXPATHLEN];
#ifdef CFSDEBUG
#endif
/*
* Make sure we have sufficient privileges.
*/
goto out;
/*
* make sure we're mounting on a directory
*/
goto out;
}
/*
* Determine the zone we're being mounted into, and make sure it's the
* global zone.
*/
if (getzoneid() == GLOBAL_ZONEID) {
goto out;
}
} else {
goto out;
}
goto out;
}
/*
* Assign a unique device id to the mount
*/
#ifdef _LP64
/*
* It's not a good idea to make fsid bigger since that'll
* have adverse effects on nfs filehandles. For now assume that
* cachefs be used on devices that fit into dev32_t's.
*/
if (cachefs_dev == NODEV) {
goto out;
}
#endif
/*
* Copy in the arguments
*/
if (error) {
goto out;
}
if ((cfs_options->opt_flags &
goto out;
}
goto out;
}
/*
* Get the cache directory vp
*/
/*LINTED 32-bit pointer casting okay*/
NULLVPP, &cachedirvp);
if (error)
goto out;
/*
* Make sure the thing we just looked up is a directory
*/
goto out;
}
/*
* Make sure the cache doesn't live in cachefs!
*/
goto out;
}
/* if the backfs is mounted */
/*LINTED 32-bit pointer casting okay*/
/*
* Get the back file system root vp
*/
NULLVPP, &backrootvp);
if (error)
goto out;
/*
* Make sure the thing we just looked up is a directory
* and a root of a file system
*/
"cachefs_mount: backpath not a directory\n");
goto out;
}
/*
* Get the fid and attributes for the root of the
* backfilesystem, except if NFSv4 is in use,
* in which case we get the attributes only (the
* (VOP_FID() operation called by cachefs_get_cookie()
* is not supported in NFSv4).
*/
} else {
}
if (error)
goto out;
} else {
backfileno = 0;
}
/*
* In SVR4 it's not acceptable to stack up mounts
* unless MS_OVERLAY specified.
*/
goto out;
}
/*
* Lock out other mounts and unmounts until we safely have
* a mounted fscache object.
*/
/*
* Find the cache structure
*/
break;
}
/* if the cache object does not exist, then create it */
if (error) {
goto out;
}
else
"CacheFS: attempt to convert nonempty cache "
"to NOFILL mode");
goto out;
}
/* get the fscache id for this name */
if (error) {
fsid = 0;
}
/* find the fscache object for this mount point or create it */
if (error) {
goto again;
}
goto out;
}
} else {
/* compare the options to make sure they are compatable */
if (error) {
"CacheFS: mount failed, options do not match.");
goto out;
}
/* copy options into the fscache */
}
error = 0;
if (fscp->fs_fscdirvp) {
/*
* If a log file exists and the cache is being mounted without
* the snr (aka disconnectable) option, return an error.
*/
if ((error == 0) &&
"disconnectable option not specified\n");
goto out;
}
}
/*
* Acquire the name of the mount point
*/
/*
* the string length returned by copystr includes the
* terminating NULL character, unless a NULL string is
* passed in, then the string length is unchanged.
*/
strl = 0;
tmpstr[0] = '\0';
if (strl > 1) {
}
/*
* else fscp->fs_mntpt is unchanged(still NULL) try again
* next time
*/
}
/*
* Acquire the name of the server
*/
strl = 0;
tmpstr[0] = '\0';
/*LINTED 32-bit pointer casting okay*/
if (strl > 1) {
}
/*
* else fscp->fs_hostname remains unchanged (is still NULL)
*/
}
/*
* Acquire name of the back filesystem
*/
strl = 0;
tmpstr[0] = '\0';
/*LINTED 32-bit pointer casting okay*/
if (strl > 1) {
}
/*
* else fscp->fs_backfsname remains unchanged (is still NULL)
*/
}
/* see if fscache object is already mounted, it not, make it so */
if (error) {
/* fs cache was already mounted */
goto out;
}
/* set nfs style time out parameters */
if (backvfsp)
else
/* make a cnode for the root of the file system */
if (error) {
goto out;
}
/* stick the root cnode in the fscache object */
/*
* Get the maxfilesize bits of the back file system.
*/
kcred);
if (error) {
"cachefs_mount: Can't get the FILESIZEBITS of the back root vnode \n");
goto out;
}
/* remove the unmount file if it is there */
/* wake up the cache worker if ANY packed pending work */
/*
* Warn that caching is disabled with NFSv4 first time around.
*/
"Cachefs has detected a mount with NFSv4: caching will"
" be disabled for this and other NFSv4 mounts\n");
}
out:
/*
* make a log entry, if appropriate
*/
/*
* Cleanup our mess
*/
if (cachedirvp != NULL)
if (backrootvp != NULL)
if (fscp)
if (attrp)
if (error) {
if (cachep) {
int xx;
/* lock the cachep's fslist */
/*
* gc isn't necessary for list_mounted(), but
* we want to do it anyway.
*/
/* if no more references to this cachep, punt it. */
if (xx == 0)
}
} else {
}
#ifdef CFSDEBUG
printf("cachefs_mount: EXIT\n");
#endif
return (error);
}
void
{
int i, rc;
goto out;
goto out;
if (backfs) {
goto out;
} else {
}
/* protect cachefs_kstat_key */
/*
* XXXX If already there, why not go straight to it?
* We know that fscp->fs_kstat_id == i + 1
*/
if ((i >= 0) && (i < cachefs_kstat_key_n))
rc = 1;
else
rc = i = 0;
for (; i < cachefs_kstat_key_n; i++) {
key = cachefs_kstat_key + i;
mountpoint) == 0 &&
cachedir) == 0 &&
break;
if (rc) { /* direct key did not work - check all */
i = -1; /* will increment to zero in loop */
rc = 0;
}
}
if (i >= cachefs_kstat_key_n) {
sizeof (cachefs_kstat_key_t), KM_SLEEP);
if (cachefs_kstat_key != NULL) {
cachefs_kstat_key_n * sizeof (*key));
cachefs_kstat_key_n * sizeof (*key));
}
} else
/* finished cachefs_kstat_key */
/*
* we must not be holding any mutex that is a ks_lock field
* for one of the kstats when we invoke kstat_create,
* kstat_install, and friends.
*/
/* really should be EVERY cachep's c_log_mutex */
/* cachefs.#.log */
}
/* cachefs.#.stats */
}
out:
if (mountpoint != NULL)
}
void
cachefs_kstat_umount(int ksid)
{
k->ks_mounted = 0;
}
int
{
int i;
if (rw == KSTAT_WRITE)
return (EIO);
return (EIO);
for (i = 0; i < cachefs_kstat_key_n; i++) {
k = key + i;
ksp->ks_data_size +=
ksp->ks_data_size +=
ksp->ks_data_size +=
ksp->ks_data_size +=
}
return (0);
}
int
{
caddr_t s;
int i;
if (rw == KSTAT_WRITE)
return (EIO);
return (0); /* paranoid */
for (i = 0; i < cachefs_kstat_key_n; i++) {
k = key + i;
s += strlen(s) + 1;
s += strlen(s) + 1;
s += strlen(s) + 1;
s += strlen(s) + 1;
}
return (0);
}
extern void cachefs_inactivate();
static int
{
int error;
int xx;
#ifdef CFSDEBUG
#endif
goto out;
/*
* forced unmount is not supported by this file system
* and thus, ENOTSUP, is being returned.
*/
goto out;
}
/* if a log file exists don't allow the unmount */
if (fscp->fs_dlogfile) {
goto out;
}
/*
* wait for the cache-wide async queue to drain. Someone
* here may be trying to sync our fscache...
*/
#ifdef CFSDEBUG
printf("unmount: waiting for cache async queue...\n");
#endif
}
if (error) {
#ifdef CFSDEBUG
printf("cachefs_unmount: "
"cachefs_async_halt error %d\n", error);
#endif
goto out;
}
/*
* No active cnodes on this cache && rootvp refcnt == 1
*/
#ifdef CFSDEBUG
printf("cachefs_unmount: busy (cnodes active %d, idle "
#endif
goto out;
}
/* get rid of anything on the idle list */
#ifdef CFSDEBUG
printf("cachefs_unmount: busy (cnode count %d)\n",
fscp->fs_cnodecnt);
#endif
goto out;
}
/* get rid of the root cnode */
#ifdef CFSDEBUG
printf("cachefs_unmount: busy (inactive failed)\n");
#endif
goto out;
}
/* create the file indicating not mounted */
else
if (xx == 0) {
} else {
}
/* sync the file system just in case */
/* lock out other unmounts and mount */
/* mark the file system as not mounted */
if (fscp->fs_kstat_id > 0)
fscp->fs_kstat_id = 0;
/* drop the inum translation table */
if (fscp->fs_inum_size > 0) {
fscp->fs_inum_size = 0;
}
/* get rid of any unused fscache objects */
/* get the number of mounts on this cache */
/* if no mounts left, deactivate the cache */
if (xx == 0)
out:
if (error) {
}
#ifdef CFSDEBUG
printf("cachefs_unmount: EXIT\n");
#endif
return (error);
}
/*
* remove the cache from the list of caches
*/
static void
{
struct cachefscache **cachepp;
int found = 0;
for (cachepp = &cachefs_cachelist;
found++;
break;
}
}
/* shut down the cache */
}
static int
{
/*LINTED alignment okay*/
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
return (0);
}
/*
* Get file system statistics.
*/
static int
{
int error;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
if (error)
return (error);
/*
* When connected return backfs stats
*/
} else {
/*
* Otherwise, just return the frontfs stats
*/
if (!error) {
}
}
if (error)
return (error);
/*
* Make sure fstype is CFS.
*/
return (0);
}
/*
* queue a request to sync the given fscache
*/
static void
{
struct cachefs_req *rp;
}
/*ARGSUSED*/
static int
{
struct cachefscache *cachep;
if (getzoneid() != GLOBAL_ZONEID)
return (EPERM);
/*
* queue an async request to do the sync.
* We always sync an entire cache (as opposed to an
* individual fscache) so that we have an opportunity
* to set the clean flag.
*/
if (vfsp) {
/*LINTED alignment okay*/
} else {
}
}
}
return (0);
}
static int
{
int error = 0;
struct cachefsoptions *cfs_options;
if (error)
goto out;
/*
* get cache directory vp
*/
NULLVPP, &cachedirvp);
if (error)
goto out;
goto out;
}
error = 0;
if (cachedirvp) {
}
/* XXX not quite right */
#if 0
/*
* If a log file exists and the cache is being mounted without
* the snr (aka disconnectable) option, return an error.
*/
if ((error == 0) &&
"cachefs_mount: log exists and disconnectable"
"option not specified\n");
goto out;
}
#endif
error = 0;
/*
* If the user is using NFSv4 and there are other options
* specified, make sure we ignore the other options.
*/
if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
}
/* XXX need mount options "nocache" and "nofill" */
/* if nocache is being turned off */
if (error)
goto out;
/* get the fsid for the fscache */
if (error)
fsid = 0;
/* activate the fscache */
if (error) {
cacheid);
goto out;
}
/* enable the cache */
}
/* else if nofill is being turn off */
/* enable the cache */
}
/* if the backfs is mounted now or we have a new backfs */
/* get the back file system root vp */
NULLVPP, &backrootvp);
if (error)
goto out;
/*
* Make sure the thing we just looked up is a directory
* and a root of a file system
*/
"cachefs_mount: backpath not a directory\n");
goto out;
}
/*
* XXX
* Kind of dangerous to just set this but we do
* not have locks around usage of fs_backvfsp.
* Hope for the best for now.
* Probably should also spin through vnodes and fix them up.
* Krishna - fixed c_backvp to reflect the change.
*/
/*
* Now the root cnode structure is an owner of
* the opened back root vnode structure; we must
* clear the pointer to back root vnode here as
* we don't need it since now, and the root cnode
* structure will control the vnode
*/
}
if (fscp->fs_kstat_id > 0)
fscp->fs_kstat_id = 0;
out:
if (cachedirvp)
if (backrootvp)
return (error);
}