smbfs_subr2.c revision 5ecede3380b4d48e616af1e5dc4671daf3aef703
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
* All rights reserved.
*/
/*
* Node hash implementation borrowed from NFS.
* See: uts/common/fs/nfs/nfs_subr.c
*/
#ifdef APPLE
#include <sys/smb_apple.h>
#include <sys/smb_iconv.h>
#else
#include <netsmb/smb_osdep.h>
#endif
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
/*
* The hash queues for the access to active and cached smbnodes
* for each hash bucket is used to control access and to synchronize
* lookups, additions, and deletions from the hash queue.
*
* The smbnode freelist is organized as a doubly linked list with
* a head pointer. Additions and deletions are synchronized via
* a single mutex.
*
* In order to add an smbnode to the free list, it must be hashed into
* a hash queue and the exclusive lock to the hash queue be held.
* If an smbnode is not hashed into a hash queue, then it is destroyed
* because it represents no valuable information that can be reused
* about the file. The exclusive lock to the hash queue must be
* held in order to prevent a lookup in the hash queue from finding
* the smbnode and using it and assuming that the smbnode is not on the
* freelist. The lookup in the hash queue will have the hash queue
* locked, either exclusive or shared.
*
* The vnode reference count for each smbnode is not allowed to drop
* below 1. This prevents external entities, such as the VM
* subsystem, from acquiring references to vnodes already on the
* freelist and then trying to place them back on the freelist
* when their reference is released. This means that the when an
* smbnode is looked up in the hash queues, then either the smbnode
* is removed from the freelist and that reference is tranfered to
* the new reference or the vnode reference count must be incremented
* accordingly. The mutex for the freelist must be held in order to
* accurately test to see if the smbnode is on the freelist or not.
* The hash queue lock might be held shared and it is possible that
* two different threads may race to remove the smbnode from the
* freelist. This race can be resolved by holding the mutex for the
* freelist. Please note that the mutex for the freelist does not
* need to held if the smbnode is not on the freelist. It can not be
* placed on the freelist due to the requirement that the thread
* putting the smbnode on the freelist must hold the exclusive lock
* to the hash queue and the thread doing the lookup in the hash
* queue is holding either a shared or exclusive lock to the hash
* queue.
*
* The lock ordering is:
*
* hash bucket lock -> vnode lock
* hash bucket lock -> freelist lock
*/
static kmutex_t smbfreelist_lock;
static ulong_t smbnodenew = 0;
long nsmbnode = 0;
static int smbtablesize;
static int smbtablemask;
static int smbhashlen = 4;
static struct kmem_cache *smbnode_cache;
/*
* Mutex to protect the following variables:
* smbfs_major
* smbfs_minor
*/
int smbfs_major;
int smbfs_minor;
/*
* Local functions.
* Not static, to aid debugging.
*/
void smb_rmfree(smbnode_t *);
void smbinactive(smbnode_t *);
void smb_rmhash_locked(smbnode_t *);
void smb_destroy_node(smbnode_t *);
void smbfs_kmem_reclaim(void *cdrarg);
/*
* Free the resources associated with an smbnode.
* Note: This is different from smbfs_inactive
*
* NFS: nfs_subr.c:rinactive
*/
void
{
}
}
/*
* Return a vnode for the given CIFS directory and filename.
* If no smbnode exists for this fhandle, create one and put it
* into the hash queues. If the smbnode for this fhandle
* already exists, return it.
*
* Note: make_smbnode() may upgrade the hash bucket lock to exclusive.
*
* NFS: nfs_subr.c:makenfsnode
*/
vnode_t *
const char *dir,
int dirlen,
const char *name,
int nmlen,
char sep,
{
char *rpath;
#ifdef NOT_YET
#endif
int newnode;
/*
* Build the full path name in allocated memory
* so we have it for lookup, etc. Note the
* special case at the root (dir=="\\", dirlen==1)
* where this does not add a slash separator.
* To do that would make a double slash, which
* has special meaning in CIFS.
*
* ToDo: Would prefer to allocate a remote path
* only when we will create a new node.
*/
/* Compute the length of rpath and allocate. */
if (sep)
rplen++;
if (name)
/* Fill in rpath */
if (sep)
if (name)
/*
* Note: make_smbnode keeps a reference to rpath in
* new nodes it creates, so only free when we found
* an existing node.
*/
if (!newnode) {
}
#ifdef NOT_YET
if (newnode) {
}
#endif
return (vp);
}
/* Have SMB attributes. */
/* XXX: np->n_ino = fap->fa_ino; see above */
/* XXX: np->r_attr = *fap here instead? */
#ifdef NOT_YET
if (!newnode) {
} else {
else
}
#else
#endif
return (vp);
}
/*
* NFS: nfs_subr.c:rtablehash
* We use smbfs_hash().
*/
/*
* Find or create an smbnode.
* NFS: nfs_subr.c:make_rnode
*/
static vnode_t *
char *rpath,
int rplen,
int *newnode)
{
*newnode = 0;
return (vp);
}
/* Note: will retake this lock below. */
/*
* see if we can find something on the freelist
*/
np = smbfreelist;
smb_rmfree(np);
goto start;
}
}
goto start;
}
vn_invalid(vp);
/*
* destroy old locks before bzero'ing and
* recreating the locks below.
*/
/*
* Make sure that if smbnode is recycled then
* VFS count is decremented properly before
* reuse.
*/
} else {
/*
* allocate and initialize a new smbnode
*/
}
/* Initialize smbnode_t */
/* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
/* XXX: make attributes stale? */
#if 0 /* XXX dircache */
/*
* We don't know if it's a directory yet.
* Let the caller do this? XXX
*/
#endif
/* Now fill in the vnode. */
/*
* There is a race condition if someone else
* alloc's the smbnode while no locks are held, so we
* check again and recover if found.
*/
*newnode = 0;
/* The node we were building goes on the free list. */
return (vp);
}
/*
* Hash search identifies nodes by the full pathname,
* so store that before linking in the hash list.
* Note: caller allocates the rpath, and knows
* about this reference when *newnode is set.
*/
*newnode = 1;
return (vp);
}
/*
* smb_addfree
* Put a smbnode on the free list.
*
* Normally called by smbfs_inactive, but also
* called in here during cleanup operations.
*
* Smbnodes which were allocated above and beyond the normal limit
* are immediately freed.
*
* NFS: nfs_subr.c:rp_addfree
*/
void
{
/*
* If we have too many smbnodes allocated and there are no
* references to this smbnode, or if the smbnode is no longer
* accessible by it does not reside in the hash queues,
* or if an i/o error occurred while writing to the file,
* then just free it instead of putting it on the smbnode
* freelist.
*/
return;
/*
* Will get another call later,
* via smbfs_inactive.
*/
}
}
/*
* Recheck the vnode reference count. We need to
* make sure that another reference has not been
* acquired while we were not holding v_lock. The
* smbnode is not in the smbnode hash queues, so the
* only way for a reference to have been acquired
* is for a VOP_PUTPAGE because the smbnode was marked
* with RDIRTY or for a modified page. This
* reference may have been acquired before our call
* to smbinactive. The i/o may have been completed,
* thus allowing smbinactive to complete, but the
* reference to the vnode may not have been released
* yet. In any case, the smbnode can not be destroyed
* until the other references to this vnode have been
* released. The other references will take care of
* either destroying the smbnode or placing it on the
* smbnode freelist. If there are no other references,
* then the smbnode may be safely destroyed.
*/
return;
}
return;
}
/*
* Lock the hash queue and then recheck the reference count
* to ensure that no other threads have acquired a reference
* to indicate that the smbnode should not be placed on the
* freelist. If another reference has been acquired, then
* just release this one and let the other thread complete
* the processing of adding this smbnode to the freelist.
*/
return;
}
/*
* If there is no cached data or metadata for this file, then
* put the smbnode on the front of the freelist so that it will
* be reused before other smbnodes which may have cached data or
* metadata associated with them.
*/
if (smbfreelist == NULL) {
smbfreelist = np;
} else {
}
}
/*
* Remove an smbnode from the free list.
*
* The caller must be holding smbfreelist_lock and the smbnode
* must be on the freelist.
*
* NFS: nfs_subr.c:rp_rmfree
*/
void
{
if (np == smbfreelist) {
if (np == smbfreelist)
smbfreelist = NULL;
}
}
/*
* Put a smbnode in the hash table.
*
* The caller must be holding the exclusive hash queue lock.
*
* NFS: nfs_subr.c:rp_addhash
*/
void
{
}
/*
* Remove a smbnode from the hash table.
*
* The caller must be holding the hash queue lock.
*
* NFS: nfs_subr.c:rp_rmhash_locked
*/
void
{
}
/*
* Remove a smbnode from the hash table.
*
* The caller must not be holding the hash queue lock.
*/
void
{
}
/*
* Lookup a smbnode by fhandle.
*
* The caller must be holding the hash queue lock, either shared or exclusive.
* XXX: make static?
*
* NFS: nfs_subr.c:rfind
*/
const char *rpath,
int rplen,
{
/*
* remove smbnode from free list, if necessary.
*/
/*
* If the smbnode is on the freelist,
* then remove it and use that reference
* as the new reference. Otherwise,
* need to increment the reference count.
*/
smb_rmfree(np);
} else {
}
} else
return (np);
}
}
return (NULL);
}
#ifdef SMB_VNODE_DEBUG
int smb_check_table_debug = 1;
#else /* SMB_VNODE_DEBUG */
int smb_check_table_debug = 0;
#endif /* SMB_VNODE_DEBUG */
/*
* Return 1 if there is a active vnode belonging to this vfs in the
* smbtable cache.
*
* Several of these checks are done without holding the usual
* locks. This is safe because destroy_smbtable(), smb_addfree(),
* etc. will redo the necessary checks before actually destroying
* any smbnodes.
*
* NFS: nfs_subr.c:check_rtable
*
* Debugging changes here relative to NFS.
* Relatively harmless, so left 'em in.
*/
int
{
int index;
int busycnt = 0;
continue; /* skip the root */
continue; /* skip other mount */
/* Now the 'busy' checks: */
/* Not on the free list? */
SMBVDEBUG("!r_freef: node=0x%p, v_path=%s\n",
busycnt++;
}
/* Has dirty pages? */
if (vn_has_cached_data(vp) &&
SMBVDEBUG("is dirty: node=0x%p, v_path=%s\n",
busycnt++;
}
/* Other refs? (not reflected in v_count) */
SMBVDEBUG("+r_count: node=0x%p, v_path=%s\n",
busycnt++;
}
if (busycnt && !smb_check_table_debug)
break;
}
}
return (busycnt);
}
/*
* Destroy inactive vnodes from the hash queues which belong to this
* vfs. It is essential that we destroy all inactive vnodes during a
* forced unmount as well as during a normal unmount.
*
* NFS: nfs_subr.c:destroy_rtable
*/
void
{
int index;
/* save the hash pointer before destroying */
smb_rmfree(np);
} else
}
}
}
/*
* This call to smb_addfree will end up destroying the
* smbnode, but in a safe way with the appropriate set
* of checks done.
*/
}
}
/*
* This routine destroys all the resources associated with the smbnode
* and then the smbnode itself.
*
* NFS: nfs_subr.c:destroy_rnode
*/
void
{
vn_invalid(vp);
}
/* rflush? */
/* access cache */
/* client handles */
/*
* initialize resources that are used by smbfs_subr.c
* this is called from the _init() routine (by the way of smbfs_clntinit())
*
* allocate and initialze smbfs hash table
* NFS: nfs_subr.c:nfs_subrinit
*/
int
smbfs_subrinit(void)
{
int i;
/*
* Allocate and initialize the smbnode hash queues
*/
if (nsmbnode <= 0)
sizeof (struct smbnode));
"setting nsmbnode to max value of %ld", nsmbnode_max);
}
for (i = 0; i < smbtablesize; i++) {
}
/*
*/
/*
* Assign unique major number for all smbfs mounts
*/
"smbfs: init: can't get unique device number");
smbfs_major = 0;
}
smbfs_minor = 0;
return (0);
}
/*
* free smbfs hash table, etc.
* NFS: nfs_subr.c:nfs_subrfini
*/
void
smbfs_subrfini(void)
{
int i;
/*
* Deallocate the smbnode hash queues
*/
for (i = 0; i < smbtablesize; i++)
/*
*/
}
/* rddir_cache ? */
/*
* Support functions for smbfs_kmem_reclaim
*/
static int
smbfs_node_reclaim(void)
{
int freed;
freed = 0;
smb_rmfree(np);
continue;
}
}
/*
* This call to smb_addfree will end up destroying the
* smbnode, but in a safe way with the appropriate set
* of checks done.
*/
}
return (freed);
}
/*
* Called by kmem_cache_alloc ask us if we could
* "Please give back some memory!"
*
* Todo: dump nodes from the free list?
*/
/*ARGSUSED*/
void
smbfs_kmem_reclaim(void *cdrarg)
{
(void) smbfs_node_reclaim();
}
/* nfs failover stuff */
/* nfs_rw_xxx - see smbfs_rwlock.c */