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