Home | History | Annotate | Download | only in nfs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <sys/systm.h>
     28 #include <sys/cmn_err.h>
     29 
     30 #include <nfs/nfs.h>
     31 
     32 #include <nfs/nfs4.h>
     33 #include <nfs/rnode4.h>
     34 #include <nfs/nfs4_clnt.h>
     35 
     36 static struct kmem_cache *svnode_cache;
     37 
     38 struct sv_stats
     39 {
     40 	int	sv_activate;
     41 	int	sv_find;
     42 	int	sv_match;
     43 	int	sv_inactive;
     44 	int	sv_exchange;
     45 } sv_stats;
     46 
     47 static int	sv_match(nfs4_fname_t *, nfs4_sharedfh_t *, svnode_t *);
     48 
     49 /*
     50  * Map a vnode back to the shadow which points to it.  This is
     51  * hard now that the vnode is not embedded in the shadow vnode.
     52  */
     53 
     54 
     55 svnode_t *
     56 vtosv(vnode_t *vp)
     57 {
     58 	rnode4_t *rp = VTOR4(vp);
     59 	svnode_t *svp, *svp_found = NULL;
     60 
     61 	/* Check to see if it's the master shadow vnode first. */
     62 
     63 	if (RTOV4(rp) == vp)
     64 		return (&rp->r_svnode);
     65 
     66 	mutex_enter(&rp->r_svlock);
     67 
     68 	for (svp = rp->r_svnode.sv_forw; svp != &rp->r_svnode;
     69 	    svp = svp->sv_forw)
     70 		if (svp->sv_r_vnode == vp) {
     71 			svp_found = svp;
     72 			break;
     73 		}
     74 
     75 	mutex_exit(&rp->r_svlock);
     76 	ASSERT(svp_found != NULL);
     77 
     78 	return (svp_found);
     79 }
     80 
     81 /*
     82  * sv_activate - find and activate the shadow vnode for the given
     83  * directory file handle and name.  May replace *vpp with a held reference
     84  * to a different vnode, in which case the reference to the previous one is
     85  * released.
     86  */
     87 
     88 void
     89 sv_activate(vnode_t **vpp, vnode_t *dvp, nfs4_fname_t **namepp, int newnode)
     90 {
     91 	svnode_t *svp;
     92 	vnode_t *resvp;
     93 	nfs4_fname_t *svpname;
     94 	rnode4_t *rp = VTOR4(*vpp);
     95 	svp = VTOSV(*vpp);
     96 
     97 	ASSERT(namepp != NULL);
     98 	ASSERT(*namepp != NULL);
     99 	ASSERT(dvp != NULL);
    100 
    101 	sv_stats.sv_activate++;
    102 
    103 	ASSERT(RW_LOCK_HELD(&rp->r_hashq->r_lock));
    104 
    105 	/*
    106 	 * If make_rnode made a new rnode (ie. newnode != 0), then
    107 	 * the master vnode was (partially) initialized there.  If
    108 	 * it was not a new rnode, then it returns the master vnode.
    109 	 * Call sv_find to find and/or initialize the shadow
    110 	 * vnode.
    111 	 */
    112 
    113 	if (newnode) {
    114 		/*
    115 		 * Initialize the shadow vnode.
    116 		 */
    117 		svp->sv_forw = svp->sv_back = svp;
    118 		ASSERT(svp->sv_dfh == NULL);
    119 		svp->sv_dfh = VTOR4(dvp)->r_fh;
    120 		sfh4_hold(svp->sv_dfh);
    121 		ASSERT(svp->sv_name == NULL);
    122 		svp->sv_name = *namepp;
    123 	} else if ((*vpp)->v_type == VREG && !((*vpp)->v_flag & VROOT)) {
    124 		resvp = sv_find(*vpp, dvp, namepp);
    125 		ASSERT(resvp->v_type == VREG);
    126 		VN_RELE(*vpp);
    127 		*vpp = resvp;
    128 	} else {
    129 		/*
    130 		 * No shadow vnodes (i.e. hard links) in this branch.
    131 		 * If sv_activate() is called for an existing rnode
    132 		 * (newnode isn't set) but with a new name, the sv_name
    133 		 * needs to be updated and the old sv_name released.
    134 		 *
    135 		 * fname mismatches can occur due to server side renames,
    136 		 * here is a chance to update the fname in case there is
    137 		 * a mismatch. Since this is not a newnode we hold r_svlock
    138 		 * to protect sv_name.
    139 		 */
    140 		mutex_enter(&rp->r_svlock);
    141 		svpname = svp->sv_name;
    142 		if (svpname != *namepp) {
    143 			/*
    144 			 * Call fn_rele() to release the hold for the
    145 			 * previous shadow vnode reference. Don't
    146 			 * release the hold on the fname pointed to by
    147 			 * namepp as we have new reference to it from
    148 			 * this shadow vnode.
    149 			 */
    150 			svp->sv_name = *namepp;
    151 			mutex_exit(&rp->r_svlock);
    152 			fn_rele(&svpname);
    153 		} else {
    154 			mutex_exit(&rp->r_svlock);
    155 			fn_rele(namepp);
    156 		}
    157 	}
    158 }
    159 
    160 /*
    161  * sv_find - find the shadow vnode for the desired name and directory
    162  * file handle.  If one does not exist, then create it.  Returns the shadow
    163  * vnode.  The caller is responsible for freeing the reference.
    164  * Consumes the name reference and nulls it out.
    165  *
    166  * Side effects: increments the reference count on the master vnode if the
    167  * shadow vnode had to be created.
    168  */
    169 
    170 vnode_t *
    171 sv_find(vnode_t *mvp, vnode_t *dvp, nfs4_fname_t **namepp)
    172 {
    173 	vnode_t *vp;
    174 	rnode4_t *rp = VTOR4(mvp);
    175 	svnode_t *svp;
    176 	svnode_t *master_svp = VTOSV(mvp);
    177 	rnode4_t *drp = VTOR4(dvp);
    178 	nfs4_fname_t *nm;
    179 
    180 	ASSERT(dvp != NULL);
    181 
    182 	sv_stats.sv_find++;
    183 
    184 	ASSERT(namepp != NULL);
    185 	ASSERT(*namepp != NULL);
    186 	nm = *namepp;
    187 	*namepp = NULL;
    188 
    189 	/*
    190 	 * At this point, all we know is that we have an rnode whose
    191 	 * file handle matches the file handle of the object we want.
    192 	 * We have to verify that component name and the directory
    193 	 * match.  If so, then we are done.
    194 	 *
    195 	 * Note: mvp is always the master vnode.
    196 	 */
    197 
    198 	ASSERT(!IS_SHADOW(mvp, rp));
    199 
    200 	if (sv_match(nm, drp->r_fh, master_svp)) {
    201 		VN_HOLD(mvp);
    202 		fn_rele(&nm);
    203 		return (mvp);
    204 	}
    205 
    206 	/*
    207 	 * No match, search through the shadow vnode list.
    208 	 * Hold the r_svlock to prevent changes.
    209 	 */
    210 
    211 	mutex_enter(&rp->r_svlock);
    212 
    213 	for (svp = master_svp->sv_forw; svp != master_svp; svp = svp->sv_forw)
    214 		if (sv_match(nm, drp->r_fh, svp)) {
    215 
    216 			/*
    217 			 * A matching shadow vnode is found, bump the
    218 			 * reference count on it and return it.
    219 			 */
    220 
    221 			vp = SVTOV(svp);
    222 			VN_HOLD(vp);
    223 			fn_rele(&nm);
    224 			mutex_exit(&rp->r_svlock);
    225 			return (vp);
    226 		}
    227 
    228 	/*
    229 	 * No match searching the list, go allocate a new shadow
    230 	 */
    231 	svp = kmem_cache_alloc(svnode_cache, KM_SLEEP);
    232 	svp->sv_r_vnode = vn_alloc(KM_SLEEP);
    233 	vp = SVTOV(svp);
    234 
    235 	/* Initialize the vnode */
    236 
    237 	vn_setops(vp, nfs4_vnodeops);
    238 	vp->v_data = (caddr_t)rp;
    239 	vp->v_vfsp = mvp->v_vfsp;
    240 	ASSERT(nfs4_consistent_type(mvp));
    241 	vp->v_type = mvp->v_type;
    242 	vp->v_pages = (page_t *)-1;	/* No pages, please */
    243 	vn_exists(vp);
    244 
    245 	/* Initialize the shadow vnode */
    246 
    247 	svp->sv_dfh = VTOR4(dvp)->r_fh;
    248 	sfh4_hold(svp->sv_dfh);
    249 
    250 	svp->sv_name = nm;
    251 	VN_HOLD(mvp);
    252 	insque(svp, master_svp);
    253 	mutex_exit(&rp->r_svlock);
    254 
    255 	return (vp);
    256 }
    257 
    258 /*
    259  * sv_match - check to see if the shadow vnode matches the desired
    260  * name and directory file handle.  Returns non-zero if there's a match,
    261  * zero if it's not a match.
    262  */
    263 
    264 static int
    265 sv_match(nfs4_fname_t *nm, nfs4_sharedfh_t *fhp, svnode_t *svp)
    266 {
    267 	sv_stats.sv_match++;
    268 
    269 	return (svp->sv_name != NULL && svp->sv_name == nm &&
    270 	    SFH4_SAME(svp->sv_dfh, fhp));
    271 }
    272 
    273 /*
    274  * sv_inactive - deactivate a shadow vnode. sv_inactive is called
    275  * from nfs4_inactive.  Whenever a shadow vnode is de-activated,
    276  * sv_inactive cleans up the mess and releases the reference on the
    277  * master vnode.
    278  */
    279 
    280 void
    281 sv_inactive(vnode_t *vp)
    282 {
    283 	svnode_t *svp;
    284 	rnode4_t *rp;
    285 	vnode_t *mvp;
    286 
    287 	sv_stats.sv_inactive++;
    288 
    289 	svp = VTOSV(vp);
    290 	rp = VTOR4(vp);
    291 	mvp = rp->r_vnode;
    292 
    293 	ASSERT(mvp != vp);
    294 
    295 	/*
    296 	 * Remove the shadow vnode from the list.  The serialization
    297 	 * is provided by the svnode list lock.  This could be done
    298 	 * with the r_statelock, but that would require more locking
    299 	 * in the activation path.
    300 	 */
    301 
    302 	mutex_enter(&rp->r_svlock);
    303 	mutex_enter(&vp->v_lock);
    304 	/* check if someone slipped in while locks were dropped */
    305 	if (vp->v_count > 1) {
    306 		vp->v_count--;
    307 		mutex_exit(&vp->v_lock);
    308 		mutex_exit(&rp->r_svlock);
    309 		return;
    310 	}
    311 	remque(svp);
    312 	mutex_exit(&vp->v_lock);
    313 	mutex_exit(&rp->r_svlock);
    314 
    315 	sv_uninit(svp);
    316 	svp->sv_forw = svp->sv_back = NULL;
    317 	kmem_cache_free(svnode_cache, svp);
    318 	vn_invalid(vp);
    319 	vn_free(vp);
    320 
    321 	/* release the reference held by this shadow on the master */
    322 
    323 	VN_RELE(mvp);
    324 }
    325 
    326 /*
    327  * sv_uninit - free any data structures allocated by the shadow vnode.
    328  */
    329 
    330 void
    331 sv_uninit(svnode_t *svp)
    332 {
    333 	if (svp->sv_name != NULL)
    334 		fn_rele(&svp->sv_name);
    335 	if (svp->sv_dfh != NULL)
    336 		sfh4_rele(&svp->sv_dfh);
    337 }
    338 
    339 /*
    340  * sv_exchange -  exchange a shadow vnode for the master vnode.  This
    341  * occurs during nfs4_open, since only the master vnode owns the files
    342  * resources (eg. pages).
    343  */
    344 
    345 void
    346 sv_exchange(vnode_t **vpp)
    347 {
    348 	vnode_t *mvp;
    349 
    350 	sv_stats.sv_exchange++;
    351 
    352 	/* RTOV always returns the master vnode */
    353 	mvp = RTOV4(VTOR4(*vpp));
    354 	VN_HOLD(mvp)
    355 	VN_RELE(*vpp);
    356 	*vpp = mvp;
    357 }
    358 
    359 int
    360 nfs4_shadow_init(void)
    361 {
    362 	/*
    363 	 * Allocate shadow vnode cache
    364 	 */
    365 	svnode_cache = kmem_cache_create("svnode_cache",
    366 	    sizeof (svnode_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
    367 
    368 	return (0);
    369 }
    370 
    371 int
    372 nfs4_shadow_fini(void)
    373 {
    374 	kmem_cache_destroy(svnode_cache);
    375 
    376 	return (0);
    377 }
    378