xref: /titanic_44/usr/src/uts/common/fs/cachefs/cachefs_strict.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
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/param.h>
30 #include <sys/types.h>
31 #include <sys/systm.h>
32 #include <sys/cred.h>
33 #include <sys/proc.h>
34 #include <sys/user.h>
35 #include <sys/vfs.h>
36 #include <sys/vnode.h>
37 #include <sys/pathname.h>
38 #include <sys/uio.h>
39 #include <sys/tiuser.h>
40 #include <sys/sysmacros.h>
41 #include <sys/kmem.h>
42 #include <netinet/in.h>
43 #include <sys/mount.h>
44 #include <sys/ioctl.h>
45 #include <sys/statvfs.h>
46 #include <sys/errno.h>
47 #include <sys/debug.h>
48 #include <sys/cmn_err.h>
49 #include <sys/utsname.h>
50 #include <sys/bootconf.h>
51 #include <sys/modctl.h>
52 
53 #include <vm/hat.h>
54 #include <vm/as.h>
55 #include <vm/page.h>
56 #include <vm/pvn.h>
57 #include <vm/seg.h>
58 #include <vm/seg_map.h>
59 #include <vm/seg_vn.h>
60 #include <vm/rm.h>
61 #include <sys/fs/cachefs_fs.h>
62 
63 #define	C_CACHE_VALID(SAVED_MTIME, NEW_MTIME)   \
64 	((SAVED_MTIME.tv_sec == NEW_MTIME.tv_sec) && \
65 		(SAVED_MTIME.tv_nsec == NEW_MTIME.tv_nsec))
66 
67 static time_t cachefs_gettime_cached_object(struct fscache *fscp,
68     struct cnode *cp, time_t mtime);
69 
70 static int
71 c_strict_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap,
72     cred_t *cr)
73 {
74 	int error;
75 	cachefs_metadata_t *mdp = &cp->c_metadata;
76 
77 	ASSERT(cr);
78 	ASSERT(MUTEX_HELD(&cp->c_statelock));
79 
80 	/* if attributes not passed in then get them */
81 	if (vap == NULL) {
82 		/* if not connected then cannot get attrs */
83 		if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
84 		    (fscp->fs_backvfsp == NULL))
85 			return (ETIMEDOUT);
86 
87 		/* get backvp if necessary */
88 		if (cp->c_backvp == NULL) {
89 			error = cachefs_getbackvp(fscp, cp);
90 			if (error)
91 				return (error);
92 		}
93 
94 		/* get the attributes */
95 		cp->c_attr.va_mask = AT_ALL;
96 		error = VOP_GETATTR(cp->c_backvp, &cp->c_attr, 0, cr);
97 		if (error)
98 			return (error);
99 	} else {
100 		/* copy passed in attributes into the cnode */
101 		cp->c_attr = *vap;
102 	}
103 
104 	/*
105 	 * Expire time is based on the number of seconds since
106 	 * the last change.
107 	 * (i.e. files that changed recently are likely to change soon)
108 	 */
109 	mdp->md_x_time.tv_nsec = 0;
110 	mdp->md_x_time.tv_sec = cachefs_gettime_cached_object(fscp, cp,
111 		cp->c_attr.va_mtime.tv_sec);
112 	mdp->md_consttype = CFS_FS_CONST_STRICT;
113 	cp->c_size = cp->c_attr.va_size;
114 	cp->c_flags |= CN_UPDATED;
115 
116 	return (0);
117 }
118 
119 static int
120 c_strict_check_cached_object(struct fscache *fscp, struct cnode *cp,
121 	int verify_what, cred_t *cr)
122 {
123 	struct vattr attrs;
124 	int error = 0;
125 	int fail = 0, backhit = 0;
126 	cachefs_metadata_t *mdp = &cp->c_metadata;
127 
128 #ifdef CFSDEBUG
129 	CFS_DEBUG(CFSDEBUG_VOPS)
130 		printf("c_strict_check_cached_object: ENTER cp %p\n",
131 		    (void *)cp);
132 #endif
133 
134 	ASSERT(cr);
135 	ASSERT(MUTEX_HELD(&cp->c_statelock));
136 
137 	if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
138 	    (fscp->fs_backvfsp == NULL))
139 		goto out;
140 
141 	/*
142 	 * If backfs is NFSv4, do a getattr to update link count,
143 	 * all other attributes are not used, and the backfs is
144 	 * called on a getattr request.
145 	 */
146 	if (CFS_ISFS_BACKFS_NFSV4(fscp)) {
147 		backhit = 1;
148 		attrs.va_mask = AT_ALL;
149 		error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
150 		if (error)
151 			goto out;
152 		cp->c_attr = attrs;
153 		goto out;
154 	}
155 
156 	/* done if do not have to check and time has not expired */
157 	if (((verify_what & C_BACK_CHECK) == 0) &&
158 	    (gethrestime_sec() < mdp->md_x_time.tv_sec) &&
159 	    ((mdp->md_flags & MD_NEEDATTRS) == 0))
160 		goto out;
161 
162 	/* get backvp if necessary */
163 	if (cp->c_backvp == NULL) {
164 		error = cachefs_getbackvp(fscp, cp);
165 		if (error)
166 			goto out;
167 	}
168 
169 	/*
170 	 * If the cnode is being populated, and we're not the populating
171 	 * thread, then block until the pop thread completes.  If we are the
172 	 * pop thread, then we may come in here, but not to nuke the directory
173 	 * cnode at a critical juncture.
174 	 */
175 again:
176 	while ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
177 	    (cp->c_popthrp != curthread)) {
178 		cv_wait(&cp->c_popcv, &cp->c_statelock);
179 
180 		/*
181 		 * recheck backvp and connectivity - if backvp now null,
182 		 * something bad happened, so don't bother trying to 'get' it
183 		 */
184 		if ((cp->c_backvp == NULL) ||
185 			(fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
186 			(fscp->fs_backvfsp == NULL)) {
187 			if (cp->c_flags | CN_STALE) {
188 				cp->c_flags |= CN_NOCACHE;
189 				error = ESTALE;
190 			}
191 			goto out;
192 		}
193 	}
194 
195 	/* get the file attributes from the back fs */
196 	attrs.va_mask = AT_ALL;
197 	error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
198 	backhit = 1;
199 	if (error)
200 		goto out;
201 
202 	/* if the mtime or size of the file has changed */
203 	if ((!C_CACHE_VALID(mdp->md_vattr.va_mtime, attrs.va_mtime) ||
204 	    (cp->c_size != attrs.va_size)) &&
205 	    ((mdp->md_flags & MD_NEEDATTRS) == 0)) {
206 		fail = 1;
207 #ifdef CFSDEBUG
208 		CFS_DEBUG(CFSDEBUG_INVALIDATE)
209 			printf("c_strict_check: invalidating %llu\n",
210 			    (u_longlong_t)cp->c_id.cid_fileno);
211 #endif
212 		if (vn_has_cached_data(CTOV(cp))) {
213 			mutex_exit(&cp->c_statelock);
214 			error = cachefs_putpage_common(CTOV(cp),
215 			    (offset_t)0, 0, B_INVAL, cr);
216 			mutex_enter(&cp->c_statelock);
217 			if (CFS_TIMEOUT(fscp, error))
218 				goto out;
219 			error = 0;
220 			/*
221 			 * if an async pop started while the lock was
222 			 * dropped, go back and try again
223 			 */
224 			if ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
225 			    (cp->c_popthrp != curthread))
226 				goto again;
227 		}
228 		/*
229 		 * We should properly handle the CN_NOCACHE flag here.
230 		 * In fact, we should remember that cachefs_inval_object()
231 		 * forcibly sets/unsets the flag, so we should keep a
232 		 * state of the flag over the call.
233 		 */
234 		if ((cp->c_flags & CN_NOCACHE) == 0)
235 			cachefs_inval_object(cp);
236 		else {
237 			cachefs_inval_object(cp);
238 			cp->c_flags |= CN_NOCACHE;
239 		}
240 		if ((CTOV(cp))->v_type == VREG) {
241 			attrs.va_mask = AT_ALL;
242 			error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
243 			if (error)
244 				goto out;
245 		}
246 		if (!vn_has_cached_data(CTOV(cp))) {
247 			cp->c_size = attrs.va_size;
248 		}
249 #ifdef CFSDEBUG
250 		else {
251 			CFS_DEBUG(CFSDEBUG_VOPS)
252 				printf("c_strict_check: v_pages not null\n");
253 		}
254 #endif
255 	}
256 
257 	/* toss cached acl info if ctime changed */
258 	if (!C_CACHE_VALID(mdp->md_vattr.va_ctime, attrs.va_ctime)) {
259 		cachefs_purgeacl(cp);
260 	}
261 
262 	cp->c_attr = attrs;
263 	if (attrs.va_size > cp->c_size)
264 		cp->c_size = attrs.va_size;
265 	mdp->md_x_time.tv_sec =
266 	    cachefs_gettime_cached_object(fscp, cp, attrs.va_mtime.tv_sec);
267 	mdp->md_flags &= ~MD_NEEDATTRS;
268 	cachefs_cnode_setlocalstats(cp);
269 	cp->c_flags |= CN_UPDATED;
270 
271 out:
272 	if (backhit != 0) {
273 		if (fail != 0)
274 			fscp->fs_stats.st_fails++;
275 		else
276 			fscp->fs_stats.st_passes++;
277 	}
278 
279 #ifdef CFSDEBUG
280 	CFS_DEBUG(CFSDEBUG_VOPS)
281 		printf("c_strict_check_cached_object: EXIT expires %lx\n",
282 			(long)mdp->md_x_time.tv_sec);
283 #endif
284 	return (error);
285 }
286 
287 static void
288 c_strict_modify_cached_object(struct fscache *fscp, struct cnode *cp,
289 	cred_t *cr)
290 {
291 	struct vattr attrs;
292 	int error = 0;
293 	nlink_t nlink;
294 	cachefs_metadata_t *mdp = &cp->c_metadata;
295 
296 	ASSERT(MUTEX_HELD(&cp->c_statelock));
297 	ASSERT(fscp->fs_cdconnected == CFS_CD_CONNECTED);
298 	ASSERT(fscp->fs_backvfsp);
299 
300 	/*
301 	 * Don't do a getattr if NFSv4, which maintains
302 	 * its attributes (and link count) by doing a call
303 	 * to CFSOP_CHECK_COBJECT() during vnode operations.
304 	 */
305 	if (CFS_ISFS_BACKFS_NFSV4(fscp))
306 		goto out;
307 
308 	fscp->fs_stats.st_modifies++;
309 
310 	/* from now on, make sure we're using the server's idea of time */
311 	mdp->md_flags &= ~(MD_LOCALCTIME | MD_LOCALMTIME);
312 	mdp->md_flags |= MD_NEEDATTRS;
313 
314 	/* if in write-around mode, make sure file is nocached */
315 	if (CFS_ISFS_WRITE_AROUND(fscp)) {
316 		if ((cp->c_flags & CN_NOCACHE) == 0)
317 			cachefs_nocache(cp);
318 
319 		/*
320 		 * If a directory, then defer getting the new attributes
321 		 * until requested.  Might be a little bit faster this way.
322 		 */
323 		if (CTOV(cp)->v_type == VDIR)
324 			goto out;
325 	}
326 
327 	/* get the new mtime so the next call to check_cobject does not fail */
328 	if (cp->c_backvp == NULL) {
329 		error = cachefs_getbackvp(fscp, cp);
330 		if (error) {
331 			mdp->md_vattr.va_mtime.tv_sec = 0;
332 			goto out;
333 		}
334 	}
335 
336 	attrs.va_mask = AT_ALL;
337 	ASSERT(cp->c_backvp != NULL);
338 	error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
339 	if (error) {
340 		mdp->md_vattr.va_mtime.tv_sec = 0;
341 		goto out;
342 	}
343 
344 	mdp->md_x_time.tv_sec =
345 	    cachefs_gettime_cached_object(fscp, cp, attrs.va_mtime.tv_sec);
346 	nlink = cp->c_attr.va_nlink;
347 	cp->c_attr = attrs;
348 	cp->c_attr.va_nlink = nlink;
349 	if ((attrs.va_size > cp->c_size) || !vn_has_cached_data(CTOV(cp)))
350 		cp->c_size = attrs.va_size;
351 	mdp->md_flags &= ~MD_NEEDATTRS;
352 	cachefs_cnode_setlocalstats(cp);
353 out:
354 	cp->c_flags |= CN_UPDATED;
355 }
356 
357 /*ARGSUSED*/
358 static void
359 c_strict_invalidate_cached_object(struct fscache *fscp, struct cnode *cp,
360 	cred_t *cr)
361 {
362 	cachefs_metadata_t *mdp = &cp->c_metadata;
363 
364 	ASSERT(MUTEX_HELD(&cp->c_statelock));
365 	mdp->md_vattr.va_mtime.tv_sec = 0;
366 	mdp->md_flags |= MD_NEEDATTRS;
367 	cp->c_flags |= CN_UPDATED;
368 }
369 
370 /*ARGSUSED*/
371 static void
372 c_strict_convert_cached_object(struct fscache *fscp, struct cnode *cp,
373 	cred_t *cr)
374 {
375 	cachefs_metadata_t *mdp = &cp->c_metadata;
376 
377 	ASSERT(MUTEX_HELD(&cp->c_statelock));
378 	mdp->md_flags |= MD_NEEDATTRS;
379 	mdp->md_consttype = CFS_FS_CONST_STRICT;
380 	cp->c_flags |= CN_UPDATED;
381 }
382 
383 /*
384  * Returns the tod in secs when the consistency of the object should
385  * be checked.
386  */
387 static time_t
388 cachefs_gettime_cached_object(struct fscache *fscp, struct cnode *cp,
389 	time_t mtime)
390 {
391 	time_t xsec;
392 	time_t acmin, acmax;
393 	time_t now;
394 
395 	/*
396 	 * Expire time is based on the number of seconds since the last change
397 	 * (i.e. files that changed recently are likely to change soon),
398 	 */
399 	if ((CTOV(cp))->v_type == VDIR) {
400 		acmin = fscp->fs_acdirmin;
401 		acmax = fscp->fs_acdirmax;
402 	} else {
403 		acmin = fscp->fs_acregmin;
404 		acmax = fscp->fs_acregmax;
405 	}
406 
407 	now = gethrestime_sec();
408 	xsec = now - mtime;
409 	xsec = MAX(xsec, acmin);
410 	xsec = MIN(xsec, acmax);
411 	xsec += now;
412 	return (xsec);
413 }
414 
415 struct cachefsops strictcfsops = {
416 	c_strict_init_cached_object,
417 	c_strict_check_cached_object,
418 	c_strict_modify_cached_object,
419 	c_strict_invalidate_cached_object,
420 	c_strict_convert_cached_object
421 };
422