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