Print this page
3749 zfs event processing should work on R/O root filesystems
Submitted by:   Justin Gibbs <justing@spectralogic.com>
Reviewed by:    Matthew Ahrens <mahrens@delphix.com>
Reviewed by:    Eric Schrock <eric.schrock@delphix.com>
        
*** 24,33 ****
--- 24,34 ----
   * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
   * Copyright (c) 2012 by Delphix. All rights reserved.
   */
  
  #include <sys/spa.h>
+ #include <sys/fm/fs/zfs.h>
  #include <sys/spa_impl.h>
  #include <sys/nvpair.h>
  #include <sys/uio.h>
  #include <sys/fs/zfs.h>
  #include <sys/vdev_impl.h>
*** 138,162 ****
                  kmem_free(buf, fsize);
  
          kobj_close_file(file);
  }
  
! static void
  spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl)
  {
          size_t buflen;
          char *buf;
          vnode_t *vp;
          int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX;
          char *temp;
  
          /*
           * If the nvlist is empty (NULL), then remove the old cachefile.
           */
          if (nvl == NULL) {
!                 (void) vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE);
!                 return;
          }
  
          /*
           * Pack the configuration into a buffer.
           */
--- 139,164 ----
                  kmem_free(buf, fsize);
  
          kobj_close_file(file);
  }
  
! static int
  spa_config_write(spa_config_dirent_t *dp, nvlist_t *nvl)
  {
          size_t buflen;
          char *buf;
          vnode_t *vp;
          int oflags = FWRITE | FTRUNC | FCREAT | FOFFMAX;
          char *temp;
+         int err;
  
          /*
           * If the nvlist is empty (NULL), then remove the old cachefile.
           */
          if (nvl == NULL) {
!                 err = vn_remove(dp->scd_path, UIO_SYSSPACE, RMFILE);
!                 return (err);
          }
  
          /*
           * Pack the configuration into a buffer.
           */
*** 173,196 ****
           * 'write to temporary file, sync, move over original' to make sure we
           * always have a consistent view of the data.
           */
          (void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path);
  
!         if (vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0) == 0) {
!                 if (vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE,
!                     0, RLIM64_INFINITY, kcred, NULL) == 0 &&
!                     VOP_FSYNC(vp, FSYNC, kcred, NULL) == 0) {
!                         (void) vn_rename(temp, dp->scd_path, UIO_SYSSPACE);
!                 }
                  (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL);
                  VN_RELE(vp);
          }
  
          (void) vn_remove(temp, UIO_SYSSPACE, RMFILE);
  
          kmem_free(buf, buflen);
          kmem_free(temp, MAXPATHLEN);
  }
  
  /*
   * Synchronize pool configuration to disk.  This must be called with the
   * namespace lock held.
--- 175,201 ----
           * 'write to temporary file, sync, move over original' to make sure we
           * always have a consistent view of the data.
           */
          (void) snprintf(temp, MAXPATHLEN, "%s.tmp", dp->scd_path);
  
!         err = vn_open(temp, UIO_SYSSPACE, oflags, 0644, &vp, CRCREAT, 0);
!         if (err == 0) {
!                 err = vn_rdwr(UIO_WRITE, vp, buf, buflen, 0, UIO_SYSSPACE,
!                     0, RLIM64_INFINITY, kcred, NULL);
!                 if (err == 0)
!                         err = VOP_FSYNC(vp, FSYNC, kcred, NULL);
!                 if (err == 0)
!                         err = vn_rename(temp, dp->scd_path, UIO_SYSSPACE);
                  (void) VOP_CLOSE(vp, oflags, 1, 0, kcred, NULL);
                  VN_RELE(vp);
          }
  
          (void) vn_remove(temp, UIO_SYSSPACE, RMFILE);
  
          kmem_free(buf, buflen);
          kmem_free(temp, MAXPATHLEN);
+         return (err);
  }
  
  /*
   * Synchronize pool configuration to disk.  This must be called with the
   * namespace lock held.
*** 198,207 ****
--- 203,214 ----
  void
  spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent)
  {
          spa_config_dirent_t *dp, *tdp;
          nvlist_t *nvl;
+         boolean_t ccw_failure;
+         int error;
  
          ASSERT(MUTEX_HELD(&spa_namespace_lock));
  
          if (rootdir == NULL || !(spa_mode_global & FWRITE))
                  return;
*** 209,218 ****
--- 216,226 ----
          /*
           * Iterate over all cachefiles for the pool, past or present.  When the
           * cachefile is changed, the new one is pushed onto this list, allowing
           * us to update previous cachefiles that no longer contain this pool.
           */
+         ccw_failure = B_FALSE;
          for (dp = list_head(&target->spa_config_list); dp != NULL;
              dp = list_next(&target->spa_config_list, dp)) {
                  spa_t *spa = NULL;
                  if (dp->scd_path == NULL)
                          continue;
*** 249,262 ****
                          VERIFY(nvlist_add_nvlist(nvl, spa->spa_name,
                              spa->spa_config) == 0);
                          mutex_exit(&spa->spa_props_lock);
                  }
  
!                 spa_config_write(dp, nvl);
                  nvlist_free(nvl);
          }
  
          /*
           * Remove any config entries older than the current one.
           */
          dp = list_head(&target->spa_config_list);
          while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) {
--- 257,292 ----
                          VERIFY(nvlist_add_nvlist(nvl, spa->spa_name,
                              spa->spa_config) == 0);
                          mutex_exit(&spa->spa_props_lock);
                  }
  
!                 error = spa_config_write(dp, nvl);
!                 if (error != 0)
!                         ccw_failure = B_TRUE;
                  nvlist_free(nvl);
          }
  
+         if (ccw_failure) {
+                 /*
+                  * Keep trying so that configuration data is
+                  * written if/when any temporary filesystem
+                  * resource issues are resolved.
+                  */
+                 if (target->spa_ccw_fail_time == 0) {
+                         zfs_ereport_post(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE,
+                             target, NULL, NULL, 0, 0);
+                 }
+                 target->spa_ccw_fail_time = gethrtime();
+                 spa_async_request(target, SPA_ASYNC_CONFIG_UPDATE);
+         } else {
+                 /*
+                  * Do not rate limit future attempts to update
+                  * the config cache.
+                  */
+                 target->spa_ccw_fail_time = 0;
+         }
+ 
          /*
           * Remove any config entries older than the current one.
           */
          dp = list_head(&target->spa_config_list);
          while ((tdp = list_next(&target->spa_config_list, dp)) != NULL) {