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