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