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