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