xref: /titanic_44/usr/src/uts/common/fs/cachefs/cachefs_cod.c (revision b65731f1f612238279eb4d997f43589b535c5646)
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(TOKEN_MTIME, NEW_MTIME)   \
64 	((TOKEN_MTIME.tv_sec == NEW_MTIME.tv_sec) && \
65 		(TOKEN_MTIME.tv_nsec == NEW_MTIME.tv_nsec))
66 
67 static int
68 c_cod_init_cached_object(fscache_t *fscp, cnode_t *cp, vattr_t *vap,
69     cred_t *cr)
70 {
71 	int error;
72 	cachefs_metadata_t *mdp = &cp->c_metadata;
73 
74 	ASSERT(cr != NULL);
75 	ASSERT(MUTEX_HELD(&cp->c_statelock));
76 
77 	/* NFSv4 option sets strict consistency */
78 	ASSERT(CFS_ISFS_BACKFS_NFSV4(fscp) == 0);
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 	cp->c_size = cp->c_attr.va_size;
105 	mdp->md_x_time = fscp->fs_cod_time;
106 	mdp->md_consttype = CFS_FS_CONST_CODCONST;
107 	cp->c_flags |= CN_UPDATED;
108 	return (0);
109 }
110 
111 static int
112 c_cod_check_cached_object(struct fscache *fscp, struct cnode *cp,
113     int verify_what, cred_t *cr)
114 {
115 	struct vattr attrs;
116 	int fail = 0, backhit = 0;
117 	int error = 0;
118 	cachefs_metadata_t *mdp = &cp->c_metadata;
119 
120 #ifdef CFSDEBUG
121 	CFS_DEBUG(CFSDEBUG_VOPS)
122 		printf("c_cod_check_cached_object: ENTER cp %p\n", cp);
123 #endif
124 	ASSERT(cr);
125 	ASSERT(MUTEX_HELD(&cp->c_statelock));
126 
127 	/* nothing to do if not connected */
128 	if ((fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
129 	    (fscp->fs_backvfsp == NULL))
130 		goto out;
131 
132 	/* done if do not have to check and cod button has not been pushed */
133 	if (((verify_what & C_BACK_CHECK) == 0) &&
134 	    (C_CACHE_VALID(mdp->md_x_time, fscp->fs_cod_time)) &&
135 	    ((mdp->md_flags & MD_NEEDATTRS) == 0))
136 		goto out;
137 
138 	/* get backvp if necessary */
139 	if (cp->c_backvp == NULL) {
140 		error = cachefs_getbackvp(fscp, cp);
141 		if (error)
142 			goto out;
143 	}
144 
145 	/*
146 	 * If the cnode is being populated, and we're not the populating
147 	 * thread, then block until the pop thread completes.  If we are the
148 	 * pop thread, then we may come in here, but not to nuke the directory
149 	 * cnode at a critical juncture.
150 	 */
151 again:
152 	while ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
153 	    (cp->c_popthrp != curthread)) {
154 		cv_wait(&cp->c_popcv, &cp->c_statelock);
155 
156 		/*
157 		 * recheck backvp and connectivity - if backvp now null,
158 		 * something bad happened, so don't bother trying to 'get' it
159 		 */
160 		if ((cp->c_backvp == NULL) ||
161 			(fscp->fs_cdconnected != CFS_CD_CONNECTED) ||
162 			(fscp->fs_backvfsp == NULL)) {
163 			if (cp->c_flags | CN_STALE) {
164 				cp->c_flags |= CN_NOCACHE;
165 				error = ESTALE;
166 			}
167 			goto out;
168 		}
169 	}
170 
171 	/* get the file attributes from the back fs */
172 	attrs.va_mask = AT_ALL;
173 	error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
174 	backhit = 1;
175 	if (error)
176 		goto out;
177 
178 	/* if the mtime or size of the file has changed */
179 	if ((!C_CACHE_VALID(mdp->md_vattr.va_mtime, attrs.va_mtime) ||
180 	    (cp->c_size != attrs.va_size)) &&
181 	    ((mdp->md_flags & MD_NEEDATTRS) == 0)) {
182 		fail = 1;
183 		if (vn_has_cached_data(CTOV(cp))) {
184 			mutex_exit(&cp->c_statelock);
185 			error = cachefs_putpage_common(CTOV(cp),
186 			    (offset_t)0, 0, B_INVAL, cr);
187 			mutex_enter(&cp->c_statelock);
188 			if (CFS_TIMEOUT(fscp, error))
189 				goto out;
190 			error = 0;
191 			/*
192 			 * if an async pop started while the lock was
193 			 * dropped, go back and try again
194 			 */
195 			if ((cp->c_flags & CN_ASYNC_POP_WORKING) &&
196 			    (cp->c_popthrp != curthread))
197 				goto again;
198 		}
199 		/*
200 		 * We should properly handle the CN_NOCACHE flag here.
201 		 * In fact, we should remember that cachefs_inval_object()
202 		 * forcibly sets/unsets the flag, so we should keep a
203 		 * state of the flag over the call.
204 		 */
205 		if ((cp->c_flags & CN_NOCACHE) == 0)
206 			cachefs_inval_object(cp);
207 		else {
208 			cachefs_inval_object(cp);
209 			cp->c_flags |= CN_NOCACHE;
210 		}
211 		if ((CTOV(cp))->v_type == VREG) {
212 			attrs.va_mask = AT_ALL;
213 			error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
214 			if (error)
215 				goto out;
216 		}
217 		if (!vn_has_cached_data(CTOV(cp))) {
218 			cp->c_size = attrs.va_size;
219 #ifdef CFSDEBUG
220 		} else {
221 			CFS_DEBUG(CFSDEBUG_VOPS)
222 				printf("c_cod_check: v_pages not null\n");
223 #endif
224 		}
225 	}
226 
227 	/* toss cached acl info if ctime changed */
228 	if (!C_CACHE_VALID(mdp->md_vattr.va_ctime, attrs.va_ctime)) {
229 		cachefs_purgeacl(cp);
230 	}
231 
232 	cp->c_attr = attrs;
233 	if (attrs.va_size > cp->c_size)
234 		cp->c_size = attrs.va_size;
235 	mdp->md_x_time = fscp->fs_cod_time;
236 	mdp->md_flags &= ~MD_NEEDATTRS;
237 	cachefs_cnode_setlocalstats(cp);
238 	cp->c_flags |= CN_UPDATED;
239 
240 out:
241 	if (backhit != 0) {
242 		if (fail != 0)
243 			fscp->fs_stats.st_fails++;
244 		else
245 			fscp->fs_stats.st_passes++;
246 	}
247 
248 #ifdef CFSDEBUG
249 	CFS_DEBUG(CFSDEBUG_VOPS)
250 		printf("c_cod_check_cached_object: EXIT\n");
251 #endif
252 
253 	return (error);
254 }
255 
256 /*ARGSUSED*/
257 static void
258 c_cod_modify_cached_object(struct fscache *fscp, struct cnode *cp, cred_t *cr)
259 {
260 	struct vattr attrs;
261 	int error = 0;
262 	nlink_t nlink;
263 	cachefs_metadata_t *mdp = &cp->c_metadata;
264 
265 	ASSERT(MUTEX_HELD(&cp->c_statelock));
266 	ASSERT(fscp->fs_cdconnected == CFS_CD_CONNECTED);
267 	ASSERT(fscp->fs_backvfsp);
268 
269 	fscp->fs_stats.st_modifies++;
270 
271 	/* from now on, make sure we're using the server's idea of time */
272 	mdp->md_flags &= ~(MD_LOCALCTIME | MD_LOCALMTIME);
273 	mdp->md_flags |= MD_NEEDATTRS;
274 
275 	/* if in write-around mode, make sure file is nocached */
276 	if (CFS_ISFS_WRITE_AROUND(fscp)) {
277 		if ((cp->c_flags & CN_NOCACHE) == 0)
278 			cachefs_nocache(cp);
279 
280 		/*
281 		 * If a directory, then defer getting the new attributes
282 		 * until requested.  Might be a little bit faster this way.
283 		 */
284 		if (CTOV(cp)->v_type == VDIR)
285 			goto out;
286 	}
287 
288 	/* get the new mtime so the next call to check_cobject does not fail */
289 	if (cp->c_backvp == NULL) {
290 		error = cachefs_getbackvp(fscp, cp);
291 		if (error) {
292 			mdp->md_vattr.va_mtime.tv_sec = 0;
293 			goto out;
294 		}
295 	}
296 	attrs.va_mask = AT_ALL;
297 	ASSERT(cp->c_backvp != NULL);
298 	error = VOP_GETATTR(cp->c_backvp, &attrs, 0, cr);
299 	if (error) {
300 		mdp->md_vattr.va_mtime.tv_sec = 0;
301 		goto out;
302 	}
303 	nlink = cp->c_attr.va_nlink;
304 	cp->c_attr = attrs;
305 	cp->c_attr.va_nlink = nlink;
306 	if ((attrs.va_size > cp->c_size) || !vn_has_cached_data(CTOV(cp)))
307 		cp->c_size = attrs.va_size;
308 	mdp->md_flags &= ~MD_NEEDATTRS;
309 	cachefs_cnode_setlocalstats(cp);
310 out:
311 	cp->c_flags |= CN_UPDATED;
312 }
313 
314 /*ARGSUSED*/
315 static void
316 c_cod_invalidate_cached_object(struct fscache *fscp, struct cnode *cp,
317     cred_t *cr)
318 {
319 	cachefs_metadata_t *mdp = &cp->c_metadata;
320 
321 	ASSERT(MUTEX_HELD(&cp->c_statelock));
322 	mdp->md_vattr.va_mtime.tv_sec = 0;
323 	mdp->md_flags |= MD_NEEDATTRS;
324 	cp->c_flags |= CN_UPDATED;
325 }
326 
327 /*ARGSUSED*/
328 static void
329 c_cod_convert_cached_object(struct fscache *fscp, struct cnode *cp,
330     cred_t *cr)
331 {
332 	cachefs_metadata_t *mdp = &cp->c_metadata;
333 
334 	ASSERT(MUTEX_HELD(&cp->c_statelock));
335 	mdp->md_flags |= MD_NEEDATTRS;
336 	mdp->md_consttype = CFS_FS_CONST_CODCONST;
337 	cp->c_flags |= CN_UPDATED;
338 }
339 
340 struct cachefsops codcfsops = {
341 	c_cod_init_cached_object,
342 	c_cod_check_cached_object,
343 	c_cod_modify_cached_object,
344 	c_cod_invalidate_cached_object,
345 	c_cod_convert_cached_object
346 };
347