Home | History | Annotate | Download | only in cachefs
      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 };
    421