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 };