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
c_strict_init_cached_object(fscache_t * fscp,cnode_t * cp,vattr_t * vap,cred_t * cr)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
c_strict_check_cached_object(struct fscache * fscp,struct cnode * cp,int verify_what,cred_t * cr)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
c_strict_modify_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)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
c_strict_invalidate_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)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
c_strict_convert_cached_object(struct fscache * fscp,struct cnode * cp,cred_t * cr)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
cachefs_gettime_cached_object(struct fscache * fscp,struct cnode * cp,time_t mtime)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