nfs4_stub_vnops.c revision eabd0450c0ea06b7993daac8f9545c7061ae7cae
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Support for ephemeral mounts, e.g. mirror-mounts. These mounts are
* triggered from a "stub" rnode via a special set of vnodeops.
*/
#include <sys/vfs_opreg.h>
#include <sys/pathname.h>
#include <sys/sysmacros.h>
#include <sys/pathconf.h>
#include <sys/systeminfo.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs4_kprot.h>
#include <nfs/nfs4_clnt.h>
#include <sys/int_fmtio.h>
/*
* The automatic unmounter thread stuff!
*/
/*
* Just a default....
*/
typedef struct nfs4_trigger_globals {
int ntg_thread_started;
static void nfs4_ephemeral_start_harvester(nfs4_trigger_globals_t *);
/*
* Used for ephemeral mounts; contains data either duplicated from
* servinfo4_t, or hand-crafted, depending on type of ephemeral mount.
*
* It's intended that this structure is used solely for ephemeral
* mount-type specific data, for passing this data to
* nfs4_trigger_nargs_create().
*/
typedef struct ephemeral_servinfo {
char *esi_hostname;
char *esi_netname;
char *esi_path;
int esi_path_len;
int esi_mount_flags;
struct netbuf *esi_syncaddr;
struct knetconfig *esi_knconf;
/*
* Collect together the mount-type specific and generic data args.
*/
typedef struct domount_args {
char *dma_hostlist; /* comma-sep. for RO failover */
/*
* The vnode ops functions for a trigger stub vnode
*/
caller_context_t *);
caller_context_t *);
caller_context_t *);
caller_context_t *);
int *, pathname_t *);
vsecattr_t *);
int);
caller_context_t *, int);
cred_t *, caller_context_t *, int);
caller_context_t *, int);
cred_t *, caller_context_t *, int);
/*
* Regular NFSv4 vnodeops that we need to reference directly
*/
caller_context_t *);
caller_context_t *, int *, pathname_t *);
caller_context_t *);
caller_context_t *);
cred_t *);
servinfo4_t *);
static void nfs4_trigger_nargs_destroy(struct nfs_args *);
static char *nfs4_trigger_create_mntopts(vfs_t *);
static void nfs4_trigger_destroy_mntopts(char *);
static int nfs4_trigger_add_mntopt(char *, char *, vfs_t *);
/*
* These are the vnodeops that we must define for stub vnodes.
*
*
* Many of the VOPs defined for NFSv4 do not need to be defined here,
* for various reasons. This will result in the VFS default function being
* used:
*
* - These VOPs require a previous VOP_OPEN to have occurred. That will have
* lost the reference to the stub vnode, meaning these should not be called:
* close, read, write, ioctl, readdir, seek.
*
* - These VOPs are meaningless for vnodes without data pages. Since the
* stub vnode is of type VDIR, these should not be called:
* space, getpage, putpage, map, addmap, delmap, pageio, fsync.
*
* - These VOPs are otherwise not applicable, and should not be called:
* dump, setsecattr.
*
*
* These VOPs we do not want to define, but nor do we want the VFS default
* action. Instead, we specify the VFS error function, with fs_error(), but
* note that fs_error() is not actually called. Instead it results in the
* use of the error function defined for the particular VOP, in vn_ops_table[]:
*
* - frlock, dispose, shrlock.
*
*
* These VOPs we define to use the corresponding regular NFSv4 vnodeop.
* NOTE: if any of these ops involve an OTW call with the stub FH, then
* that call must be wrapped with save_mnt_secinfo()/check_mnt_secinfo()
* to protect the security data in the servinfo4_t for the "parent"
* filesystem that contains the stub.
*
* - These VOPs should not trigger a mount, so that "ls -l" does not:
* pathconf, getsecattr.
*
* - These VOPs would not make sense to trigger:
* inactive, rwlock, rwunlock, fid, realvp.
*/
const fs_operation_def_t nfs4_trigger_vnodeops_template[] = {
};
/*
* Trigger ops for stub vnodes; for mirror mounts, etc.
*
* The general idea is that a "triggering" op will first call
* nfs4_trigger_mount(), which will find out whether a mount has already
* been triggered.
*
* If it has, then nfs4_trigger_mount() sets newvp to the root vnode
* of the covering vfs.
*
* If a mount has not yet been triggered, nfs4_trigger_mount() will do so,
* and again set newvp, as above.
*
* The triggering op may then re-issue the VOP by calling it on newvp.
*
* Note that some ops may perform custom action, and may or may not need
* to trigger a mount.
*
* Some ops need to call the regular NFSv4 vnodeop for a stub vnode. We
* obviously can't do this with VOP_<whatever>, since it's a stub vnode
* and that would just recurse. Instead, we call the v4 op directly,
* by name. This is OK, since we know that the vnode is for NFSv4,
* otherwise it couldn't be a stub.
*
*/
static int
{
int error;
if (error)
return (error);
/* Release the stub vnode, as we're losing the reference to it */
/* Give the caller the root vnode of the newly-mounted fs */
/* return with VN_HELD(newvp) */
}
/*
* For the majority of cases, nfs4_trigger_getattr() will not trigger
* a mount. However, if ATTR_TRIGGER is set, we are being informed
* that we need to force the mount before we attempt to determine
* the attributes. The intent is an atomic operation for security
* testing.
*/
static int
{
int error;
if (flags & ATTR_TRIGGER) {
if (error)
return (error);
} else {
}
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
/* for now, we only support mirror-mounts */
/*
* It's not legal to lookup ".." for an fs root, so we mustn't pass
* that up. Instead, pass onto the regular op, regardless of whether
* we've triggered a mount.
*/
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
int flags)
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
/*
* We don't check whether svp is a stub. Let the NFSv4 code
* detect that error, and return accordingly.
*/
return (error);
}
static int
{
int error;
/*
* We know that sdvp is a stub, otherwise we would not be here.
*
* If tdvp is also be a stub, there are two possibilities: it
* is either the same stub as sdvp [i.e. VN_CMP(sdvp, tdvp)]
* or it is a different stub [!VN_CMP(sdvp, tdvp)].
*
* In the former case, just trigger sdvp, and treat tdvp as
* though it were not a stub.
*
* In the latter case, it might be a different stub for the
* same server fs as sdvp, or for a different server fs.
* Regardless, from the client perspective this would still
* be a cross-filesystem rename, and should not be allowed,
* so return EXDEV, without triggering either mount.
*/
return (EXDEV);
if (error)
return (error);
return (error);
}
/* ARGSUSED */
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
static int
{
int error;
if (error)
return (error);
return (error);
}
/* end of trigger vnode ops */
/*
* Mount upon a trigger vnode; for mirror-mounts, etc.
*
* The mount may have already occurred, via another thread. If not,
* assemble the location information - which may require fetching - and
* perform the mount.
*
* Sets newvp to be the root of the fs that is now covering vp. Note
* that we return with VN_HELD(*newvp).
*
* The caller is responsible for passing the VOP onto the covering fs.
*/
static int
{
int error;
/* for now, we only support mirror-mounts */
/*
* Has the mount already occurred?
*/
if (error)
goto done;
/* the mount has already occurred */
if (!error) {
/* need to update the reference time */
if (mi->mi_ephemeral)
}
goto done;
}
/*
* We need to lock down the ephemeral tree.
*/
is_building = TRUE;
/*
* We need to add it to the zone specific list for
* automatic unmounting and harvesting of deadwood.
*/
/*
* No lock order confusion with mi_lock because no
* other node could have grabbed net_tree_lock.
*/
} else {
net->net_refcnt++;
/*
* Note that we do not do any checks to
* see if the parent has been nuked.
* We count on the vfs layer having protected
* us from feet shooters.
*/
}
must_unlock = TRUE;
goto done;
}
/*
* Need to be root for this call to make mount work.
* Note that since we define mirror mounts to work
* for any user, we allow the mount to proceed. And
* we realize that the server will perform security
* checks to make sure that the client is allowed
* access. Finally, once the mount takes place,
* directory permissions will ensure that the
* content is secure.
*/
if (!error)
done:
if (must_unlock) {
if (is_building)
net->net_refcnt--;
}
return (error);
}
/*
* Collect together both the generic & mount-type specific args.
*/
static domount_args_t *
{
int nointr;
char *hostlist;
/* check if the current server is responding */
if (status == RPC_SUCCESS) {
return (NULL);
}
} else {
/* current server did not respond */
nargs_head = NULL;
}
nargs = nargs_head;
/*
* NFS RO failover.
*
* If we have multiple servinfo4 structures, linked via sv_next,
* we must create one nfs_args for each, linking the nfs_args via
* nfs_ext_u.nfs_extB.next.
*
* We need to build a corresponding esi for each, too, but that is
* used solely for building nfs_args, and may be immediately
* discarded, as domount() requires the info from just one esi,
* but all the nfs_args.
*
* Currently, the NFS mount code will hang if not all servers
* requested are available. To avoid that, we need to ping each
* server, here, and remove it from the list if it is not
* responding. This has the side-effect of that server then
* being permanently unavailable for this failover mount, even if
* it recovers. That's unfortunate, but the best we can do until
* the mount code path is fixed.
*/
/*
* If the current server was down, loop indefinitely until we find
* at least one responsive server.
*/
do {
/* no locking needed for sv_next; it is only set at fs mount */
/*
* nargs_head: the head of the nfs_args list
* nargs: the current tail of the list
* next: the newly-created element to be added
*/
/*
* We've already tried the current server, above;
* if it was responding, we have already included it
* and it may now be ignored.
*
* Otherwise, try it again, since it may now have
* recovered.
*/
continue;
continue;
}
/* check if the server is responding */
/* if the server did not respond, ignore it */
if (status != RPC_SUCCESS)
continue;
continue;
/*
* If the original current server (mi_curr_serv)
* was down when when we first tried it,
* (i.e. esi_first == NULL),
* we select this new server (svp) to be the server
* that we will actually contact (esi_first).
*
* Note that it's possible that mi_curr_serv == svp,
* if that mi_curr_serv was down but has now recovered.
*/
nargs_head = next;
} else {
/* esi was only needed for hostname & nargs */
}
}
/* if we've had no response at all, wait a second */
return (dma);
}
static void
{
do {
}
}
}
/*
* The ephemeral_servinfo_t struct contains basic information we will need to
* perform the mount. Whilst the structure is generic across different
* types of ephemeral mount, the way we gather its contents differs.
*/
static ephemeral_servinfo_t *
{
/* Call the ephemeral type-specific routine */
if (RP_ISSTUB_MIRRORMOUNT(rp))
else
/* for now, we only support mirror-mounts */
return (esi);
}
static void
{
/* for now, we only support mirror-mounts */
/* Currently, no need for an ephemeral type-specific routine */
/*
* The contents of ephemeral_servinfo_t goes into nfs_args,
* and will be handled by nfs4_trigger_nargs_destroy().
* We need only free the structure itself.
*/
}
/*
* Some of this may turn out to be common with other ephemeral types,
* in which case it should be moved to nfs4_trigger_esi_create(), or a
* common function called.
*/
static ephemeral_servinfo_t *
{
char *stubpath;
/* initially set to be our type of ephemeral mount; may be added to */
/*
* We're copying info from the stub rnode's servinfo4, but
* we must create new copies, not pointers, since this information
* is to be associated with the new mount, which will be
* unmounted (and its structures freed) separately
*/
/*
* Sizes passed to kmem_[z]alloc here must match those freed
* in nfs4_free_args()
*/
/*
* We hold sv_lock across kmem_zalloc() calls that may sleep, but this
* is difficult to avoid: as we need to read svp to calculate the
* sizes to be allocated.
*/
(char *)svkncp->knc_protofmly);
/*
* Used when AUTH_DH is negotiated.
*
* This is ephemeral mount-type specific, since it contains the
* server's time-sync syncaddr.
*/
/*
* We need to copy from a dh_k4_clntdata_t
* netname/netnamelen pair to a NUL-terminated
* netname string suitable for putting in nfs_args,
* where the latter has no netnamelen field.
*/
}
} else {
}
stubpath += 1;
/* for nfs_args->fh */
stubpath -= 1;
/* stubpath allocated by fn_path() */
return (esi);
}
/*
* Assemble the args, and call the generic VFS mount function to
* finally perform the ephemeral mount.
*/
static int
{
const char *orig_mntpt;
int retval;
int mntpt_len;
int spec_len;
/* first, construct the mount point for the ephemeral mount */
if (*orig_path == '.')
orig_path++;
/*
* Get rid of zone's root path
*/
if (zone != global_zone) {
/*
* -1 for trailing '/' and -1 for EOS.
*/
}
}
if (*path == '.')
path++;
path++;
/* We are going to have to add this in */
if (!has_leading_slash)
spec_len++;
/* We need to get the ':' for dma_hostlist:esi_path */
spec_len++;
/* fstype-independent mount options not covered elsewhere */
/* copy parent's mount(1M) "-m" flag */
/* not needed for MS_SYSSPACE */
/* use optptr to pass in extra mount options */
goto done;
}
/* domount() expects us to count the trailing NUL */
if (retval == 0)
done:
return (retval);
}
/*
* Build an nfs_args structure for passing to domount().
*
* Ephemeral mount-type specific data comes from the ephemeral_servinfo_t;
* generic data - common to all ephemeral mount types - is read directly
* from the parent mount's servinfo4_t and mntinfo4_t, via the stub vnode.
*/
static struct nfs_args *
{
/* setup the nfs args */
/* for AUTH_DH by negotiation */
}
/* general mount settings, all copied from parent mount */
/* add any specific flags for this type of ephemeral mount */
/*
* Security data & negotiation policy.
*
* We need to preserve the parent mount's preference for security
* negotiation, translating SV4_TRYSECDEFAULT -> NFSMNT_SECDEFAULT.
*
* If SV4_TRYSECDEFAULT is not set, that indicates that a specific
* security flavour was requested, with data in sv_secdata, and that
* no negotiation should occur. If this specified flavour fails, that's
* it. We will copy sv_secdata, and not set NFSMNT_SECDEFAULT.
*
* If SV4_TRYSECDEFAULT is set, then we start with a passed-in
* default flavour, in sv_secdata, but then negotiate a new flavour.
* Possible flavours are recorded in an array in sv_secinfo, with
* currently in-use flavour pointed to by sv_currsec.
*
* If sv_currsec is set, i.e. if negotiation has already occurred,
* we will copy sv_currsec. Otherwise, copy sv_secdata. Regardless,
* we will set NFSMNT_SECDEFAULT, to enable negotiation.
*/
/* enable negotiation for ephemeral mount */
/*
* As a starting point for negotiation, copy parent
* mount's negotiated flavour (sv_currsec) if available,
* or its passed-in flavour (sv_secdata) if not.
*/
else
} else {
/* do not enable negotiation; copy parent's passed-in flavour */
else
}
/* for NFS RO failover; caller will set if necessary */
return (nargs);
}
static void
{
/*
* Either the mount failed, in which case the data is not needed, or
* nfs4_mount() has either taken copies of what it needs or,
* where it has merely copied the ptr, it has set *our* ptr to NULL,
* whereby nfs4_free_args() will ignore it.
*/
}
/*
* When we finally get into the mounting, we need to add this
* node to the ephemeral tree.
*
* This is called from nfs4_mount().
*/
void
{
/*
* Get this before grabbing anything else!
*/
if (!ntg->ntg_thread_started) {
}
/*
* We need to tack together the ephemeral mount
* with this new mntinfo.
*/
/*
* We need to tell the ephemeral mount when
* to time out.
*/
/*
* If the enclosing mntinfo4 is also ephemeral,
* then we need to point to its enclosing parent.
* Else the enclosing mntinfo4 is the enclosing parent.
*
* We also need to weave this ephemeral node
* into the tree.
*/
/*
* We need to decide if we are
* the root node of this branch
* or if we are a sibling of this
* branch.
*/
} else {
}
} else {
/*
* The parent mntinfo4 is the non-ephemeral
* root of the ephemeral tree. We
* need to decide if we are the root
* node of that tree or if we are a
* sibling of the root node.
*
* We are the root if there is no
* other node.
*/
} else {
}
}
}
/*
* Commit the changes to the ephemeral tree for removing this node.
*/
static void
{
nfs4_ephemeral_t *e = eph;
/*
* If this branch root was not the
* tree root, then we need to fix back pointers.
*/
if (prior) {
} else {
}
if (peer)
} else if (peer) {
} else {
}
}
/*
* We want to avoid recursion at all costs. So we need to
* unroll the tree. We do this by a depth first traversal to
* leaf nodes. We blast away the leaf and work our way back
* up and down the tree.
*/
static int
{
nfs4_ephemeral_t *e = eph;
mntinfo4_t *mi;
int error;
/*
* We use the loop while unrolling the ephemeral tree.
*/
for (;;) {
/*
* First we walk down the child.
*/
if (e->ne_child) {
prior = e;
e = e->ne_child;
continue;
}
/*
* If we are the root of the branch we are removing,
* we end it here. But if the branch is the root of
* the tree, we have to forge on. We do not consider
* the peer list for the root because while it may
* be okay to remove, it is both extra work and a
* potential for a false-positive error to stall the
* unmount attempt.
*/
return (0);
/*
* Next we walk down the peer list.
*/
if (e->ne_peer) {
prior = e;
e = e->ne_peer;
continue;
}
/*
* We can only remove the node passed in by the
* caller if it is the root of the ephemeral tree.
* Otherwise, the caller will remove it.
*/
return (0);
/*
* Okay, we have a leaf node, time
* to prune it!
*
* Note that prior can only be NULL if
* and only if it is the root of the
* ephemeral tree.
*/
/*
* Cleared by umount2_engine.
*/
/*
* Inform nfs4_unmount to not recursively
* descend into this node's children when it
* gets processed.
*/
if (error) {
/*
* We need to reenable nfs4_unmount's ability
* to recursively descend on this node.
*/
return (error);
}
/*
* If we are the current node, we do not want to
* touch anything else. At this point, the only
* way the current node can have survived to here
* is if it is the root of the ephemeral tree and
* we are unmounting the enclosing mntinfo4.
*/
if (e == eph) {
return (0);
}
/*
* Stitch up the prior node. Note that since
* we have handled the root of the tree, prior
* must be non-NULL.
*/
} else {
}
e = prior;
}
/* NOTREACHED */
}
/*
* Common code to safely release net_cnt_lock and net_tree_lock
*/
void
{
if (*pmust_unlock) {
net->net_refcnt--;
*pmust_unlock = FALSE;
}
}
/*
* While we may have removed any child or sibling nodes of this
* ephemeral node, we can not nuke it until we know that there
* were no actived vnodes on it. This will do that final
* work once we know it is not busy.
*/
void
{
/*
* Now we need to get rid of the ephemeral data if it exists.
*/
if (mi->mi_ephemeral) {
/*
* If we are the root node of an ephemeral branch
* which is being removed, then we need to fixup
* pointers into and out of the node.
*/
}
}
/*
* Unmount an ephemeral node.
*/
int
{
int error = 0;
int is_derooting = FALSE;
int is_recursed = FALSE;
/*
* The active vnodes on this file system may be ephemeral
* children. We need to check for and try to unmount them
* here. If any can not be unmounted, we are going
* to return EBUSY.
*/
/*
* If an ephemeral tree, we need to check to see if
* the lock is already held. If it is, then we need
* to see if we are being called as a result of
* the recursive removal of some node of the tree or
* if we are another attempt to remove the tree.
*
* mi_flags & MI4_EPHEMERAL indicates an ephemeral
* node. mi_ephemeral being non-NULL also does this.
*
* mi_ephemeral_tree being non-NULL is sufficient
* to also indicate either it is an ephemeral node
* or the enclosing mntinfo4.
*
* Do we need MI4_EPHEMERAL? Yes, it is useful for
* when we delete the ephemeral node and need to
* differentiate from an ephemeral node and the
* enclosing root node.
*/
return (0);
}
/*
* If this is not recursion, then we need to
* grab a ref count.
*
* But wait, we also do not want to do that
* if a harvester thread has already grabbed
* the lock.
*/
if (!is_recursed) {
if (net->net_status &
/*
* Someone is already working on
* it. We need to back off and
* let them proceed.
*
* We return EBUSY so that the
* caller knows something is
* going on. Note that by that
* time, the umount in the other
* thread may have already occured.
*/
return (EBUSY);
} else
net->net_refcnt++;
}
/*
* If we grab the lock, it means that no other
* operation is working on the tree. If we don't
* grab it, we need to decide if this is because
* we are a recursive call or a new operation.
*
* If we are a recursive call, we proceed without
* the lock.
*
* Else we have to wait until the lock becomes free.
*/
if (!is_recursed) {
if (net->net_status &
net->net_refcnt--;
goto is_busy;
}
/*
* We can't hold any other locks whilst
* we wait on this to free up.
*/
/*
* Note that while mi->mi_ephemeral
* may change and thus we have to
* update eph, it is the case that
* we have tied down net and
* do not care if mi->mi_ephemeral_tree
* has changed.
*/
/*
* Okay, we need to see if either the
* tree got nuked or the current node
* got nuked. Both of which will cause
* an error.
*
* Note that a subsequent retry of the
* umount shall work.
*/
if (net->net_status &
net->net_refcnt--;
goto is_busy;
}
*pmust_unlock = TRUE;
}
} else {
/*
* If we grab it right away, everything must
* be great!
*/
*pmust_unlock = TRUE;
}
/*
* Only once we have grabbed the lock can we mark what we
* are planning on doing to the ephemeral tree.
*/
if (*pmust_unlock) {
/*
* Check to see if we are nuking the root.
*/
if (is_derooting)
net->net_status |=
}
if (!is_derooting) {
/*
* Only work on children if the caller has not already
* done so.
*/
if (!is_recursed) {
if (error)
goto is_busy;
}
} else {
/*
* Only work if there is something there.
*/
if (eph) {
if (error) {
net->net_status &=
goto is_busy;
}
/*
* Nothing else which goes wrong will
* invalidate the blowing away of the
* ephmeral tree.
*/
}
/*
* We have derooted and we have caused the tree to be
* invalid.
*/
net->net_refcnt--;
/*
* At this point, the tree should no
* longer be associated with the
* mntinfo4. We need to pull it off
* there and let the harvester take
* care of it once the refcnt drops.
*/
}
return (0);
return (error);
}
/*
* Do the umount and record any error in the parent.
*/
static void
{
int error;
if (error) {
if (prior) {
else
}
}
}
/*
* For each tree in the forest (where the forest is in
* effect all of the ephemeral trees for this zone),
* scan to see if a node can be unmounted. Note that
* unlike nfs4_ephemeral_unmount_engine(), we do
* not process the current node before children or
* siblings. I.e., if a node can be unmounted, we
* do not recursively check to see if the nodes
* hanging off of it can also be unmounted.
*
* Instead, we delve down deep to try and remove the
* children first. Then, because we share code with
* nfs4_ephemeral_unmount_engine(), we will try
* them again. This could be a performance issue in
* the future.
*
* Also note that unlike nfs4_ephemeral_unmount_engine(),
* we do not halt on an error. We will not remove the
* current node, but we will keep on trying to remove
* the others.
*
* force indicates that we want the unmount to occur
* even if there is something blocking it.
*
* time_check indicates that we want to see if the
* mount has expired past mount_to or not. Typically
* we want to do this and only on a shutdown of the
* zone would we want to ignore the check.
*/
static void
{
nfs4_ephemeral_t *e;
int flag;
mntinfo4_t *mi;
if (force)
else
flag = 0;
net->net_refcnt++;
/*
* Let the unmount code know that the
* tree is already locked!
*/
/*
* If the intent is force all ephemeral nodes to
* be unmounted in this zone, we can short circuit a
* lot of tree traversal and simply zap the root node.
*/
if (force) {
/*
* Cleared by umount2_engine.
*/
goto check_done;
}
}
if (e)
while (e) {
if (e->ne_state == NFS4_EPHEMERAL_VISIT_CHILD) {
if (e->ne_child) {
e = e->ne_child;
e->ne_state =
}
continue;
} else if (e->ne_state ==
if (e->ne_peer) {
e = e->ne_peer;
e->ne_state =
}
continue;
} else if (e->ne_state ==
/*
* If a child reported an error, do
* not bother trying to unmount.
*
* If your prior node is a parent,
* pass the error up such that they
* also do not try to unmount.
*
* However, if your prior is a sibling,
* let them try to unmount if they can.
*/
if (prior) {
else
}
/*
* Clear the error and if needed, process peers.
*
* Once we mask out the error, we know whether
* or we have to process another node.
*/
e->ne_state &= ~NFS4_EPHEMERAL_CHILD_ERROR;
if (e->ne_state == NFS4_EPHEMERAL_PROCESS_ME)
e = prior;
continue;
} else if (e->ne_state ==
if (prior) {
else
}
/*
* Clear the error from this node and do the
* correct processing.
*/
e->ne_state &= ~NFS4_EPHEMERAL_PEER_ERROR;
continue;
}
e->ne_state = NFS4_EPHEMERAL_OK;
/*
* It must be the case that we need to process
* this node.
*/
if (!time_check ||
/*
* Cleared by umount2_engine.
*/
/*
* Note that we effectively work down to the
* leaf nodes first, try to unmount them,
* then work our way back up into the leaf
* nodes.
*
* Also note that we deal with a lot of
* complexity by sharing the work with
* the manual unmount code.
*/
e, prior);
}
e = prior;
}
/*
* Are we done with this tree?
*/
net->net_refcnt--;
if (prev)
else
continue;
}
net->net_refcnt--;
}
}
}
/*
* This is the thread which decides when the harvesting
* can proceed and when to kill it off for this zone.
*/
static void
{
for (;;) {
/*
* zone is exiting...
*/
if (timeleft != -1) {
zthread_exit();
/* NOTREACHED */
}
/*
* Only bother scanning if there is potential
* work to be done.
*/
continue;
/*
* Now scan the list and get rid of everything which
* is old.
*/
}
/* NOTREACHED */
}
/*
* The zone specific glue needed to start the unmount harvester.
*
* Note that we want to avoid holding the mutex as long as possible,
* hence the multiple checks.
*
* The caller should avoid us getting down here in the first
* place.
*/
static void
{
/*
* It got started before we got here...
*/
if (ntg->ntg_thread_started)
return;
if (ntg->ntg_thread_started) {
return;
}
/*
* Start the unmounter harvester thread for this zone.
*/
ntg, 0, minclsyspri);
}
/*ARGSUSED*/
static void *
{
/*
* This is the default....
*/
return (ntg);
}
/*
* Try a nice gentle walk down the forest and convince
* all of the trees to gracefully give it up.
*/
/*ARGSUSED*/
static void
{
if (!ntg)
return;
}
/*
* Race along the forest and rip all of the trees out by
* their rootballs!
*/
/*ARGSUSED*/
static void
{
if (!ntg)
return;
}
/*
* This is the zone independent cleanup needed for
* emphemeral mount processing.
*/
void
nfs4_ephemeral_fini(void)
{
(void) zone_key_delete(nfs4_ephemeral_key);
}
/*
* This is the zone independent initialization needed for
* emphemeral mount processing.
*/
void
nfs4_ephemeral_init(void)
{
NULL);
}
/*
* nfssys() calls this function to set the per-zone
* value of mount_to to drive when an ephemeral mount is
* timed out. Each mount will grab a copy of this value
* when mounted.
*/
void
{
}
/*
* Walk the list of v4 mount options; if they are currently set in vfsp,
* append them to a new comma-separated mount option string, and return it.
*
* Caller should free by calling nfs4_trigger_destroy_mntopts().
*/
static char *
{
uint_t i;
char *mntopts;
/* get the list of applicable mount options for v4; locks *vswp */
continue;
return (NULL);
}
}
/*
* MNTOPT_XATTR is not in the v4 mount opt proto list,
* and it may only be passed via MS_OPTIONSTR, so we
* must handle it here.
*
* Ideally, it would be in the list, but NFS does not specify its
* own opt proto list, it uses instead the default one. Since
* not all filesystems support extended attrs, it would not be
* appropriate to add it there.
*/
return (NULL);
}
return (mntopts);
}
static void
{
if (mntopts)
}
/*
* Check a single mount option (optname). Add to mntopts if it is set in VFS.
*/
static int
{
return (EINVAL);
/* +1 for ',', +1 for NUL */
return (EOVERFLOW);
/* first or subsequent mount option? */
if (*mntopts != '\0')
}
return (0);
}
static enum clnt_stat
{
/* as per recov_newserver() */
max_msgsize = 0;
retries = 1;
if (error)
return (RPC_FAILED);
if (nointr)
timeout);
if (nointr)
return (status);
}