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