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