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