nfs4_state.c revision 62b9fcbec4374c6afb65f926146fe77743ae3d08
/*
* 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"
#include <sys/pathname.h>
extern u_longlong_t nfs4_srv_caller_id;
extern time_t rfs4_start_time;
extern uint_t nfs4_srv_vkey;
0,
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
0xffffffff,
{
(char)0xff, (char)0xff, (char)0xff, (char)0xff,
(char)0xff, (char)0xff, (char)0xff, (char)0xff,
(char)0xff, (char)0xff, (char)0xff, (char)0xff
}
};
/* For embedding the cluster nodeid into our clientid */
#define CLUSTER_NODEID_SHIFT 24
#define CLUSTER_MAX_NODEID 255
#ifdef DEBUG
int rfs4_debug;
#endif
/*
*/
void
{
swp->sw_wait_count = 0;
}
void
{
}
void
{
swp->sw_wait_count++;
swp->sw_wait_count--;
}
}
void
{
if (swp->sw_wait_count != 0)
}
/*
* CPR callback id -- not related to v4 callbacks
*/
static callb_id_t cpr_id = 0;
static void
{
}
}
static void
{
}
static void
{
return;
}
case OPEN_DELEGATE_NONE:
return;
case OPEN_DELEGATE_READ:
break;
case OPEN_DELEGATE_WRITE:
break;
}
}
static void
{
return;
case OPEN_DELEGATE_NONE:
return;
case OPEN_DELEGATE_READ:
break;
case OPEN_DELEGATE_WRITE:
break;
}
}
}
void
{
case OP_LOCK:
break;
case OP_OPEN:
default:
break;
}
}
void
{
/* Handle responses that need deep copy */
case OP_LOCK:
break;
case OP_OPEN:
break;
default:
break;
};
}
/*
* This is the implementation of the underlying state engine. The
* public interface to this engine is described by
* nfs4_state.h. Callers to the engine should hold no state engine
* locks when they call in to it. If the protocol needs to lock data
* structures it should do so after acquiring all references to them
* first and then follow the following lock order:
*
* client > openowner > state > lo_state > lockowner > file.
*
* Internally we only allow a thread to hold one hash bucket lock at a
* time and the lock is higher in the lock order (must be acquired
* first) than the data structure that is on that hash list.
*
* If a new reference was acquired by the caller, that reference needs
* to be released after releasing all acquired locks with the
* corresponding rfs4_*_rele routine.
*/
/*
* This code is some what prototypical for now. Its purpose currently is to
* implement the interfaces sufficiently to finish the higher protocol
* elements. This will be replaced by a dynamically resizeable tables
* backed by kmem_cache allocator. However synchronization is handled
* correctly (I hope) and will not change by much. The mutexes for
* the hash buckets that can be used to create new instances of data
* structures might be good candidates to evolve into reader writer
* locks. If it has to do a creation, it would be holding the
* mutex across a kmem_alloc with KM_SLEEP specified.
*/
#ifdef DEBUG
#define TABSIZE 17
#else
#define TABSIZE 2047
#endif
/* Used to serialize lookups of clientids */
static krwlock_t rfs4_findclient_lock;
/*
* For now this "table" is exposed so that the CPR callback
* function can tromp through it..
*/
static rfs4_index_t *rfs4_clientid_idx;
static rfs4_index_t *rfs4_nfsclnt_idx;
static rfs4_table_t *rfs4_openowner_tab;
static rfs4_index_t *rfs4_openowner_idx;
static rfs4_table_t *rfs4_state_tab;
static rfs4_index_t *rfs4_state_idx;
static rfs4_index_t *rfs4_state_owner_file_idx;
static rfs4_index_t *rfs4_state_file_idx;
static rfs4_table_t *rfs4_lo_state_tab;
static rfs4_index_t *rfs4_lo_state_idx;
static rfs4_index_t *rfs4_lo_state_owner_idx;
static rfs4_table_t *rfs4_lockowner_tab;
static rfs4_index_t *rfs4_lockowner_idx;
static rfs4_index_t *rfs4_lockowner_pid_idx;
static rfs4_table_t *rfs4_file_tab;
static rfs4_index_t *rfs4_file_idx;
static rfs4_table_t *rfs4_deleg_state_tab;
static rfs4_index_t *rfs4_deleg_idx;
static rfs4_index_t *rfs4_deleg_state_idx;
/* The values below are rfs4_lease_time units */
#ifdef DEBUG
#define CLIENT_CACHE_TIME 1
#define OPENOWNER_CACHE_TIME 1
#define STATE_CACHE_TIME 1
#define LO_STATE_CACHE_TIME 1
#define LOCKOWNER_CACHE_TIME 1
#define FILE_CACHE_TIME 3
#define DELEG_STATE_CACHE_TIME 1
#else
#define CLIENT_CACHE_TIME 10
#define OPENOWNER_CACHE_TIME 5
#define STATE_CACHE_TIME 1
#define LO_STATE_CACHE_TIME 1
#define LOCKOWNER_CACHE_TIME 3
#define FILE_CACHE_TIME 40
#define DELEG_STATE_CACHE_TIME 1
#endif
static time_t rfs4_client_cache_time = 0;
static time_t rfs4_openowner_cache_time = 0;
static time_t rfs4_state_cache_time = 0;
static time_t rfs4_lo_state_cache_time = 0;
static time_t rfs4_lockowner_cache_time = 0;
static time_t rfs4_file_cache_time = 0;
static time_t rfs4_deleg_state_cache_time = 0;
static void rfs4_dss_remove_cpleaf(rfs4_client_t *);
static void rfs4_dss_remove_leaf(rfs4_servinst_t *, char *, char *);
static void rfs4_client_destroy(rfs4_entry_t);
static uint32_t clientid_hash(void *);
static void *clientid_mkkey(rfs4_entry_t);
static uint32_t nfsclnt_hash(void *);
static void *nfsclnt_mkkey(rfs4_entry_t);
static void rfs4_openowner_destroy(rfs4_entry_t);
static uint32_t openowner_hash(void *);
static void *openowner_mkkey(rfs4_entry_t);
static void rfs4_state_destroy(rfs4_entry_t);
static uint32_t state_hash(void *);
static void *state_mkkey(rfs4_entry_t);
static uint32_t state_owner_file_hash(void *);
static void *state_owner_file_mkkey(rfs4_entry_t);
static uint32_t state_file_hash(void *);
static void *state_file_mkkey(rfs4_entry_t);
static void rfs4_lo_state_destroy(rfs4_entry_t);
static uint32_t lo_state_hash(void *);
static void *lo_state_mkkey(rfs4_entry_t);
static uint32_t lo_state_lo_hash(void *);
static void *lo_state_lo_mkkey(rfs4_entry_t);
static void rfs4_lockowner_destroy(rfs4_entry_t);
static uint32_t lockowner_hash(void *);
static void *lockowner_mkkey(rfs4_entry_t);
static void *pid_mkkey(rfs4_entry_t);
static void rfs4_file_destroy(rfs4_entry_t);
static void *file_mkkey(rfs4_entry_t);
static void rfs4_deleg_state_destroy(rfs4_entry_t);
static uint32_t deleg_hash(void *);
static void *deleg_mkkey(rfs4_entry_t);
static uint32_t deleg_state_hash(void *);
static void *deleg_state_mkkey(rfs4_entry_t);
static void rfs4_state_rele_nounlock(rfs4_state_t *);
static int rfs4_ss_enabled = 0;
extern void (*rfs4_client_clrst)(struct nfs4clrst_args *);
void
{
}
static rfs4_ss_pn_t *
{
/*
* validate we have a resonable path
* (account for the '/' and trailing null)
*/
return (NULL);
}
/* Handy pointer to just the leaf name */
return (ss_pn);
}
/*
* Move the "leaf" filename from "sdir" directory
* to the "ddir" directory. Return the pathname of
* the destination unless the rename fails in which
* case we need to return the source pathname.
*/
static rfs4_ss_pn_t *
{
return (NULL);
return (NULL);
}
/*
* If the rename fails we shall return the src
* pathname and free the dst. Otherwise we need
* to free the src and return the dst pathanme.
*/
return (src);
}
return (dst);
}
static rfs4_oldstate_t *
{
return (NULL);
/*
* open the state file.
*/
return (NULL);
}
return (NULL);
}
if (err) {
/*
* We don't have read access? better get the heck out.
*/
return (NULL);
}
/*
* get the file size to do some basic validation
*/
if (kill_file) {
}
return (NULL);
}
/*
* build iovecs to read in the file_version, verifier and id_len
*/
uio.uio_loffset = 0;
return (NULL);
}
/*
* if the file_version doesn't match or if the
* id_len is zero or the combination of the verifier,
* id_len and id_val is bigger than the file we have
* a problem. If so ditch the file.
*/
if (kill_file) {
}
return (NULL);
}
/*
* now get the client id value
*/
return (NULL);
}
return (cl_ss);
}
#ifdef nextdp
#endif
/*
* Add entries from statedir to supplied oldstate list.
* Optionally, move all entries from statedir -> destdir.
*/
void
{
offset_t dirchunk_offset = 0;
/*
* open the state directory
*/
return;
goto out;
/*
* Get and process the directory entries
*/
while (!dir_eof) {
if (err)
goto out;
/*
* Process all the directory entries in this
* readdir chunk
*/
/*
* Skip '.' and '..'
*/
continue;
continue;
} else {
}
} else {
}
}
}
out:
if (dirt)
}
static void
rfs4_ss_init(void)
{
int npaths = 1;
char *default_dss_path = NFS4_DSS_VAR_DIR;
/* read the default stable storage state */
rfs4_ss_enabled = 1;
}
static void
rfs4_ss_fini(void)
{
}
}
/*
* Remove all oldstate files referenced by this servinst.
*/
static void
{
return;
/* skip dummy entry */
}
/* free dummy entry */
}
/*
* Form the state and oldstate paths, and read in the stable storage files.
*/
void
{
int i;
for (i = 0; i < npaths; i++) {
/*
* Populate the current server instance's oldstate list.
*
* 1. Read stable storage data from old state directory,
* leaving its contents alone.
*
* 2. Read stable storage data from state directory,
* and move the latter's contents to old state
* directory.
*/
}
}
/*
* Check if we are still in grace and if the client can be
* granted permission to perform reclaims.
*/
void
{
/*
* It should be sufficient to check the oldstate data for just
* this client's instance. However, since our per-instance
* client grouping is solely temporal, HA-NFSv4 RG failover
* might result in clients of the same RG being partitioned into
* separate instances.
*
* Until the client grouping is improved, we must check the
* oldstate data for all instances with an active grace period.
*
* This also serves as the mechanism to remove stale oldstate data.
* The first time we check an instance after its grace period has
* expired, the oldstate data should be cleared.
*
* Start at the current instance, and walk the list backwards
* to the first.
*/
/* if the above check found this client, we're done */
if (cp->can_reclaim)
break;
}
}
static void
{
/* short circuit everything if this server instance has no oldstate */
return;
/*
* If this server instance is no longer in a grace period then
* the client won't be able to reclaim. No further need for this
* instance's oldstate data, so it can be cleared.
*/
if (!rfs4_servinst_in_grace(sip))
return;
/* this instance is still in grace; search for the clientid */
/* skip dummy entry */
break;
}
}
}
}
/*
* Place client information into stable storage: 1/3.
* First, generate the leaf filename, from the client's IP address and
* the server-generated short-hand clientid.
*/
void
{
uchar_t *b;
if (rfs4_ss_enabled == 0) {
return;
}
buf[0] = 0;
return;
}
/*
* Convert the caller's IP address to a dotted string
*/
sizeof (struct sockaddr_in));
b[1] & 0xFF, b[2] & 0xFF, b[3] & 0xFF);
struct sockaddr_in6 *sin6;
sizeof (struct sockaddr_in6));
}
}
/*
* Place client information into stable storage: 2/3.
* DSS: distributed stable storage: the file may need to be written to
* multiple directories.
*/
static void
{
/*
* It should be sufficient to write the leaf file to (all) DSS paths
* associated with just this client's instance. However, since our
* per-instance client grouping is solely temporal, HA-NFSv4 RG
* failover might result in us losing DSS data.
*
* Until the client grouping is improved, we must write the DSS data
* to all instances' paths. Start at the current instance, and
* walk the list backwards to the first.
*/
/* write the leaf file to all DSS paths */
for (i = 0; i < npaths; i++) {
/* HA-NFSv4 path might have been failed-away from us */
continue;
}
}
}
/*
* Place client information into stable storage: 3/3.
* Write the stable storage data to the requested file.
*/
static void
{
int ioflag;
int file_vers = NFS4_SS_VERSION;
char *dir;
/* allow 2 extra bytes for '/' & NUL */
/* rfs4_ss_pnalloc takes its own copy */
return;
CRCREAT, 0)) {
return;
}
/*
* We need to record leaf - i.e. the filename - so that we know
* what to remove, in the future. However, the dir part of cp->ss_pn
* should never be referenced directly, since it's potentially only
* one of several paths with this leaf in it.
*/
/* we've already recorded *this* leaf */
} else {
/* replace with this leaf */
}
} else {
}
/*
* Build a scatter list that points to the nfs_client_id4
*/
uio.uio_loffset = 0;
NFS4_VERIFIER_SIZE + sizeof (uint_t);
/* write the full client id to the file. */
}
/*
* DSS: distributed stable storage.
* Unpack the list of paths passed by nfsd.
* Use nvlist_alloc(9F) to manage the data.
* The caller is responsible for allocating and freeing the buffer.
*/
int
{
int error;
/*
* If this is a "warm start", i.e. we previously had DSS paths,
* preserve the old paths.
*/
if (rfs4_dss_paths != NULL) {
/*
* Before we lose the ptr, destroy the nvlist and pathnames
* array from the warm start before this one.
*/
if (rfs4_dss_oldpaths)
}
/* unpack the buffer into a searchable nvlist */
if (error)
return (error);
/*
* Search the nvlist for the pathnames nvpair (which is the only nvpair
* in the list, and record its location.
*/
return (error);
}
/*
* Ultimately the nfssys() call NFS4_CLR_STATE endsup here
* to find and mark the client for forced expire.
*/
static void
{
struct sockaddr_in6 *ent_sin6;
struct sockaddr_in *ent_sin;
return;
}
case AF_INET6:
/* copyin the address from user space */
break;
}
/*
* now compare, and if equivalent mark entry
* for forced expiration
*/
}
break;
case AF_INET:
/* copyin the address from user space */
break;
}
/*
* now compare, and if equivalent mark entry
* for forced expiration
*/
}
break;
default:
/* force this assert to fail */
}
}
/*
* This is called from nfssys() in order to clear server state
* for the specified client IP Address.
*/
void
{
}
/*
* Used to initialize the NFSv4 server's state or database. All of
* the tables are created and timers are set. Only called when NFSv4
* service is provided.
*/
void
{
int start_grace;
extern boolean_t rfs4_cpr_callb(void *, int);
char *dss_path = NFS4_DSS_VAR_DIR;
/*
* If the server state database has already been initialized,
* skip it
*/
if (rfs4_server_state != NULL) {
return;
}
/*
* Set the boot time. If the server
* has been restarted quickly and has had the opportunity to
* service clients, then the start_time needs to be bumped
* regardless. A small window but it exists...
*/
if (rfs4_start_time != gethrestime_sec())
else
/* DSS: distributed stable storage: initialise served paths list */
/*
* Create the first server instance, or a new one if the server has
* been restarted; see above comments on rfs4_start_time. Don't
* start its grace period; that will be done later, to maximise the
* clients' recovery window.
*/
start_grace = 0;
/* reset the "first NFSv4 request" status */
/*
* Add a CPR callback so that we can update client
* access times to extend the lease after a suspend
*/
/* set the various cache timers for table creation */
if (rfs4_client_cache_time == 0)
if (rfs4_openowner_cache_time == 0)
if (rfs4_state_cache_time == 0)
if (rfs4_lo_state_cache_time == 0)
if (rfs4_lockowner_cache_time == 0)
if (rfs4_file_cache_time == 0)
if (rfs4_deleg_state_cache_time == 0)
/* Create the overall database to hold all server state */
/* Now create the individual tables */
"Client",
2,
sizeof (rfs4_client_t),
"nfs_client_id4", nfsclnt_hash,
TRUE);
"client_id", clientid_hash,
FALSE);
"OpenOwner",
1,
sizeof (rfs4_openowner_t),
MAXTABSZ, 100);
"open_owner4", openowner_hash,
"OpenStateID",
3,
sizeof (rfs4_state_t),
MAXTABSZ, 100);
"Openowner-File",
"State-id", state_hash,
"File", state_file_hash,
FALSE);
"LockStateID",
2,
sizeof (rfs4_lo_state_t),
MAXTABSZ, 100);
"lockownerxstate",
"State-id",
"Lockowner",
2,
sizeof (rfs4_lockowner_t),
MAXTABSZ, 100);
"lock_owner4", lockowner_hash,
"pid", pid_hash,
FALSE);
"File",
1,
NULL,
sizeof (rfs4_file_t),
MAXTABSZ, -1);
"Filehandle", file_hash,
"DelegStateID",
2,
sizeof (rfs4_deleg_state_t),
MAXTABSZ, 100);
"DelegByFileClient",
deleg_mkkey, TRUE);
"DelegState",
/*
* Init the stable storage.
*/
rfs4_ss_init();
}
/*
* Used at server shutdown to cleanup all of the NFSv4 server's structures
* and other state.
*/
void
{
if (rfs4_server_state == NULL) {
return;
}
/*
* Cleanup the CPR callback.
*/
if (cpr_id)
(void) callb_delete(cpr_id);
/* First stop all of the reaper threads in the database */
/* clean up any dangling stable storage structures */
rfs4_ss_fini();
/* Reset the cache timers for next time */
rfs4_file_cache_time = 0;
/* destroy server instances and current instance ptr */
/* reset the "first NFSv4 request" status */
/* DSS: distributed stable storage */
if (rfs4_dss_oldpaths)
if (rfs4_dss_paths)
}
typedef union {
struct {
} impl_id;
} cid;
typedef union {
struct {
} cv_impl;
static uint32_t
clientid_hash(void *key)
{
}
static bool_t
{
}
static void *
{
}
static uint32_t
nfsclnt_hash(void *key)
{
int i;
hash <<= 1;
}
return (hash);
}
static bool_t
{
return (FALSE);
nfs_client->id_len) == 0);
}
static void *
{
return (&client->nfs_client);
}
static bool_t
{
return (TRUE);
/*
* If the sysadmin has used clear_locks for this
* entry then forced_expire will be set and we
* want this entry to be reaped. Or the entry
* has exceeded its lease period.
*/
> rfs4_lease_time));
return (cp_expired);
}
/*
* Remove the leaf file from all distributed stable storage paths.
*/
static void
{
}
static void
{
for (i = 0; i < npaths; i++) {
/* the HA-NFSv4 path might have been failed-over away from us */
continue;
/* allow 3 extra bytes for two '/' & a NUL */
}
}
static void
{
/* free callback info */
if (cp->cp_confirmed)
/* check if the stable storage files need to be removed */
}
/* Free the client supplied client id */
}
static bool_t
{
/* Get a clientid to give to the client */
/* If we are booted as a cluster node, embed our nodeid */
if (cluster_bootflags & CLUSTER_BOOTED)
/* Allocate and copy client's client id value */
/* Init the value for the SETCLIENTID_CONFIRM verifier */
/* An F_UNLKSYS has been done for this client */
/* We need the client to ack us */
/* TRUE all the time until the callback path actually fails */
/* Initialize the access time to now */
/* set up the callback control structure */
/*
* Associate the client_t with the current server instance.
* The hold is solely to satisfy the calling requirement of
* rfs4_servinst_assign(). In this case it's not strictly necessary.
*/
return (TRUE);
}
/*
* associated with a client. This is done during the SETCLIENTID
* processing.
*/
void
{
/* Init the value for the SETCLIENTID_CONFIRM verifier */
}
void
{
}
{
if (oldcp) {
} else {
}
if (oldcp)
return (cp);
}
{
/* If we're a cluster and the nodeid isn't right, short-circuit */
return (NULL);
return (NULL);
} else {
return (cp);
}
}
{
/*
* If the admin has executed clear_locks for this
* client id, force expire will be set, so no need
* to calculate anything because it's "outa here".
*/
if (cp->forced_expire) {
} else {
}
/*
* If the lease has expired we will also want
* to remove any stable storage state data. So
* mark the client id accordingly.
*/
return (rc);
}
void
{
if (!cp->forced_expire)
}
static bool_t
{
return (FALSE);
return (FALSE);
return (rc);
}
static uint_t
openowner_hash(void *key)
{
int i;
hash <<= 4;
}
return (hash);
}
static bool_t
{
}
void *
{
}
static bool_t
{
return (TRUE);
> rfs4_lease_time));
}
static void
{
/* Remove open owner from client's lists of open owners */
/* One less reference to the client */
/* Free the last reply for this lock owner */
}
/* Free the lock owner id */
}
void
{
}
static bool_t
{
return (FALSE);
/* Insert openowner into client's open owner list */
return (TRUE);
}
{
return (op);
}
void
{
op->open_seqid++;
}
void
{
/* Save the filehandle if provided and free if not used */
} else {
}
}
}
static bool_t
{
return (FALSE);
return (FALSE);
}
void *
{
}
static uint32_t
lockowner_hash(void *key)
{
int i;
hash <<= 4;
}
return (hash);
}
static uint32_t
{
}
static void *
{
}
static bool_t
{
}
static void
{
/* Free the lock owner id */
}
void
{
}
/* ARGSUSED */
static bool_t
{
/*
* Since expiry is called with no other references on
* this struct, go ahead and have it removed.
*/
return (TRUE);
}
static bool_t
{
return (FALSE);
/* Reference client */
return (TRUE);
}
{
return (lo);
}
{
return (lo);
}
static uint32_t
{
}
static void *
{
}
static bool_t
{
}
static void
{
}
}
/*
* Used to unlock the underlying dbe struct only
*/
void
{
}
/*
* Used to unlock the file rw lock and the file's dbe entry
* Only used to pair with rfs4_findfile_withlock()
*/
void
{
}
typedef struct {
static bool_t
{
}
&fp->delegationlist;
}
return (TRUE);
}
{
&arg, RFS4_DBS_VALID);
else {
if (fp) {
} else {
}
}
}
return (fp);
}
/*
* Find a file in the db and once it is located, take the rw lock.
* Need to check the vnode pointer and if it does not exist (it was
* removed between the db location and check) redo the find. This
* assumes that a file struct that has a NULL vnode pointer is marked
* at 'invalid' and will not be found in the db the second time
* around.
*/
{
if (fp) {
} else {
}
}
} else {
&arg, RFS4_DBS_VALID);
goto retry;
}
}
}
return (fp);
}
static uint32_t
lo_state_hash(void *key)
{
}
static bool_t
{
return (rc);
}
static void *
{
}
static bool_t
{
return (TRUE);
return (TRUE);
> rfs4_lease_time));
}
static void
{
/* Make sure to release the file locks */
/* Is the PxFS kernel module loaded? */
if (lm_remove_file_locks != NULL) {
int new_sysid;
/* Encode the cluster nodeid in new sysid */
/*
* This PxFS routine removes file locks for a
* client over all nodes of a cluster.
*/
int, new_sysid);
} else {
}
}
}
&lsp->lockownerlist;
/* Free the last reply for this state */
}
static bool_t
{
/* Attached the supplied lock owner */
&lsp->lockownerlist;
return (TRUE);
}
void
{
}
static rfs4_lo_state_t *
{
return (lsp);
}
static uint32_t
lo_state_lo_hash(void *key)
{
}
static bool_t
{
}
static void *
{
return (u_entry);
}
{
return (lsp);
}
static stateid_t
{
/*
* If we are booted as a cluster node, embed our nodeid.
* We've already done sanity checks in rfs4_client_create() so no
* need to repeat them here.
*/
clconf_get_nodeid() : 0;
return (id);
}
/*
* For use only when booted as a cluster node.
* Returns TRUE if the embedded nodeid indicates that this stateid was
* generated on another node.
*/
static int
{
}
/*
* For use only when booted as a cluster node.
* Returns TRUE if the embedded nodeid indicates that this clientid was
* generated on another node.
*/
static int
{
}
/*
* For use only when booted as a cluster node.
* Embed our cluster nodeid into the clientid.
*/
static void
{
int clnodeid;
/*
* Currently, our state tables are small enough that their
* ids will leave enough bits free for the nodeid. If the
* tables become larger, we mustn't overwrite the id.
* Equally, we only have room for so many bits of nodeid, so
* must check that too.
*/
}
static uint32_t
state_hash(void *key)
{
}
static bool_t
{
return (rc);
}
static void *
{
}
static void
{
/* release any share locks for this stateid if it's still open */
/* Were done with the file */
/* And now with the openowner */
}
static void
{
}
void
{
}
static uint32_t
deleg_hash(void *key)
{
}
static bool_t
{
}
static void *
{
return (u_entry);
}
static uint32_t
deleg_state_hash(void *key)
{
}
static bool_t
{
return (FALSE);
return (rc);
}
static void *
{
}
static bool_t
{
return (TRUE);
> rfs4_lease_time)) {
return (TRUE);
}
return (FALSE);
}
static bool_t
{
dsp->time_revoked = 0;
/* Insert state on per open owner's list */
return (TRUE);
}
static void
{
/* Were done with the file */
/* And now with the openowner */
}
{
return (dsp);
}
{
return (dsp);
}
void
{
}
void
{
/*
* If we are skipping sequence id checking, this means that
* this is the first lock request and therefore the sequence
* id does not need to be updated. This only happens on the
* first lock request for a lockowner
*/
if (!lsp->skip_seqid_check)
}
void
{
}
void
{
if (invalidate == TRUE)
}
}
static uint32_t
state_owner_file_hash(void *key)
{
}
static bool_t
{
return (FALSE);
}
static void *
{
return (u_entry);
}
static uint32_t
state_file_hash(void *key)
{
}
static bool_t
{
return (FALSE);
}
static void *
{
}
{
return (sp);
}
/* This returns ANY state struct that refers to this file */
static rfs4_state_t *
{
}
static bool_t
{
return (TRUE);
> rfs4_lease_time))
return (TRUE);
> rfs4_lease_time));
}
static bool_t
{
/* Insert state on per open owner's list */
return (TRUE);
}
static rfs4_state_t *
{
return (sp);
}
void
{
/* Remove the associated lo_state owners */
if (!lock_held)
/*
* If refcnt == 0, the dbe is about to be destroyed.
* lock state will be released by the reaper thread.
*/
}
}
if (!lock_held)
}
/*
* Remove all state associated with the given client.
*/
void
{
}
}
void
{
/* Mark client as going away. */
/* Release the client */
}
{
/*
* If we are booted as a cluster node, check the embedded nodeid.
* If it indicates that this clientid was generated on another node,
* inform the client accordingly.
*/
return (NFS4ERR_STALE_CLIENTID);
/*
* If the server start time matches the time provided
* by the client (via the clientid) and this is NOT a
* setclientid_confirm then return EXPIRED.
*/
return (NFS4ERR_EXPIRED);
return (NFS4ERR_STALE_CLIENTID);
}
/*
* This is used when a stateid has not been found amongst the
* current server's state. Check the stateid to see if it
* was from this server instantiation or not.
*/
static nfsstat4
{
/* If we are booted as a cluster node, was stateid locally generated? */
return (NFS4ERR_STALE_STATEID);
/* If types don't match then no use checking further */
return (NFS4ERR_BAD_STATEID);
/* From a previous server instantiation, return STALE */
return (NFS4ERR_STALE_STATEID);
/*
* From this server but the state is most likely beyond lease
* timeout: return NFS4ERR_EXPIRED. However, there is the
* case of a delegation stateid. For delegations, there is a
* case where the state can be removed without the client's
* revocation, the delegation state will be removed and will
* not be found. If the client does something like a
* that has been revoked, the server should return BAD_STATEID
* instead of the more common EXPIRED error.
*/
return (NFS4ERR_BAD_STATEID);
else
return (NFS4ERR_EXPIRED);
}
return (NFS4ERR_BAD_STATEID);
}
/*
* Used later on to find the various state structs. When called from
* rfs4_check_stateid()->rfs4_get_all_state(), no file struct lock is
* respect to performance.
*/
static nfsstat4
{
/* If we are booted as a cluster node, was stateid locally generated? */
return (NFS4ERR_STALE_STATEID);
}
else
return (NFS4ERR_EXPIRED);
}
return (NFS4_OK);
}
{
}
int
{
return (NFS4_CHECK_STATEID_EXPIRED);
/* Stateid is some time in the future - that's bad */
return (NFS4_CHECK_STATEID_BAD);
return (NFS4_CHECK_STATEID_REPLAY);
/* Stateid is some time in the past - that's old */
return (NFS4_CHECK_STATEID_OLD);
/* Caller needs to know about confirmation before closure */
return (NFS4_CHECK_STATEID_UNCONFIRMED);
return (NFS4_CHECK_STATEID_CLOSED);
return (NFS4_CHECK_STATEID_OKAY);
}
int
{
return (NFS4_CHECK_STATEID_EXPIRED);
/* Stateid is some time in the future - that's bad */
return (NFS4_CHECK_STATEID_BAD);
return (NFS4_CHECK_STATEID_REPLAY);
/* Stateid is some time in the past - that's old */
return (NFS4_CHECK_STATEID_OLD);
return (NFS4_CHECK_STATEID_OKAY);
}
{
/* If we are booted as a cluster node, was stateid locally generated? */
return (NFS4ERR_STALE_STATEID);
}
return (NFS4ERR_EXPIRED);
}
return (NFS4_OK);
}
{
/* If we are booted as a cluster node, was stateid locally generated? */
return (NFS4ERR_STALE_STATEID);
}
return (NFS4ERR_EXPIRED);
}
return (NFS4_OK);
}
static nfsstat4
{
case OPENID:
break;
case DELEGID:
break;
case LOCKID:
}
break;
default:
}
}
return (status);
}
/*
* Given the I/O mode (FREAD or FWRITE), this checks whether the
* rfs4_state_t struct has access to do this operation and if so
* return NFS4_OK; otherwise the proper NFSv4 error is returned.
*/
{
}
/*
* If we have OPENed the file with DENYing access
* to both READ and WRITE then no one else could
* have OPENed the file, hence no conflicting READ
* deny. This check is merely an optimization.
*/
goto out;
/* Check against file struct's DENY mode */
int deny_read = 0;
/*
* Check if any other open owner has the file
* OPENed with deny READ.
*/
deny_read = 1;
}
}
} else {
/* Illegal I/O mode */
}
out:
return (stat);
}
/*
* Given the I/O mode (FREAD or FWRITE), the vnode, the stateid and whether
* the file is being truncated, return NFS4_OK if allowed or appropriate
* V4 error if not. Note NFS4ERR_DELAY will be returned and a recall on
* the associated file will be done if the I/O is not consistent with any
* delegation in effect on the file. Should be holding VOP_RWLOCK, either
* as reader or writer as appropriate. rfs4_op_open will acquire the
* VOP_RWLOCK as writer when setting up delegation. If the stateid is bad
* this routine will return NFS4ERR_BAD_STATEID. In addition, through the
* deleg parameter, we will return whether a write delegation is held by
* the client associated with this stateid.
* If the server instance associated with the relevant client is in its
* grace period, return NFS4ERR_GRACE.
*/
{
}
return (NFS4_OK);
return (NFS4_OK);
}
return (NFS4ERR_DELAY);
}
return (NFS4_OK);
} else {
return (stat);
/* Is associated server instance in its grace period? */
return (NFS4ERR_GRACE);
}
/* Seqid in the future? - that's bad */
return (NFS4ERR_BAD_STATEID);
}
/* Seqid in the past? - that's old */
return (NFS4ERR_OLD_STATEID);
}
/* Ensure specified filehandle matches */
return (NFS4ERR_BAD_STATEID);
}
}
}
}
/* Stateid provided was an "open" stateid */
/* Is associated server instance in its grace period? */
return (NFS4ERR_GRACE);
}
/* Seqid in the future? - that's bad */
return (NFS4ERR_BAD_STATEID);
}
/* Seqid in the past - that's old */
return (NFS4ERR_OLD_STATEID);
}
}
/* Ensure specified filehandle matches */
return (NFS4ERR_BAD_STATEID);
}
return (NFS4ERR_BAD_STATEID);
}
return (NFS4ERR_OLD_STATEID);
}
if (do_access)
else
/*
* Return whether this state has write
* delegation if desired
*/
if (deleg &&
/*
* We got a valid stateid, so we update the
* lease on the client. Ideally we would like
* to do this after the calling op succeeds,
* but for now this will be good
* enough. Callers of this routine are
* currently insulated from the state stuff.
*/
/*
* If a delegation is present on this file and
* this is a WRITE, then update the lastwrite
* time to indicate that activity is present.
*/
}
return (stat);
}
/* Is associated server instance in its grace period? */
return (NFS4ERR_GRACE);
}
return (NFS4ERR_BAD_STATEID);
}
/* Ensure specified filehandle matches */
return (NFS4ERR_BAD_STATEID);
}
/*
* Return whether this state has write
* delegation if desired
*/
if (deleg &&
/*
* If a delegation is present on this file and
* this is a WRITE, then update the lastwrite
* time to indicate that activity is present.
*/
}
/*
* XXX - what happens if this is a WRITE and the
* delegation type of for READ.
*/
return (stat);
}
/*
* If we got this far, something bad happened
*/
return (NFS4ERR_BAD_STATEID);
}
}
/*
* This is a special function in that for the file struct provided the
* file. The prime use of this would be with OP_REMOVE to force the
* release of state and particularly of file locks.
*
* There is an assumption that there is no delegations outstanding on
* this file at this point. The caller should have waited for those
* to be returned or revoked.
*/
void
{
#ifdef DEBUG
/* only applies when server is handing out delegations */
if (rfs4_deleg_policy != SRV_NEVER_DELEGATE)
#endif
/* No delegations for this file */
/* Make sure that it can not be found */
return;
}
/*
* Hold as writer to prevent other server threads from
* processing requests related to the file while all state is
* being removed.
*/
/* Remove ALL state from the file */
}
/*
* This is only safe since there are no further references to
* the file.
*/
}
/* Finally let other references to proceed */
}
/*
* This function is used as a target for the rfs4_dbe_walk() call
* below. The purpose of this function is to see if the
* lockowner_state refers to a file that resides within the exportinfo
* export. If so, then remove the lock_owner state (file locks and
* share "locks") for this object since the intent is the server is
* unexporting the specified directory. Be sure to invalidate the
* object after the state has been released
*/
static void
{
}
}
/*
* This function is used as a target for the rfs4_dbe_walk() call
* below. The purpose of this function is to see if the state refers
* to a file that resides within the exportinfo export. If so, then
* remove the open state for this object since the intent is the
* server is unexporting the specified directory. The main result for
* this type of entry is to invalidate it such it will not be found in
* the future.
*/
static void
{
}
}
/*
* This function is used as a target for the rfs4_dbe_walk() call
* below. The purpose of this function is to see if the state refers
* to a file that resides within the exportinfo export. If so, then
* remove the deleg state for this object since the intent is the
* server is unexporting the specified directory. The main result for
* this type of entry is to invalidate it such it will not be found in
* the future.
*/
static void
{
}
}
/*
* This function is used as a target for the rfs4_dbe_walk() call
* below. The purpose of this function is to see if the state refers
* to a file that resides within the exportinfo export. If so, then
* release vnode hold for this object since the intent is the server
* is unexporting the specified directory. Invalidation will prevent
* this struct from being found in the future.
*/
static void
{
/*
* don't leak monitors and remove the reference
* put on the vnode when the delegation was granted.
*/
(void *)fp);
(void *)fp);
}
}
}
}
/*
* state in the server that refers to objects residing underneath this
* particular export. The ordering of the release is important.
* Lock_owner, then state and then file.
*/
void
{
if (rfs4_server_state == NULL) {
return;
}
}