xfs: replace xfs_sb_version checks with feature flag checks
[linux-2.6-microblaze.git] / fs / xfs / xfs_mount.c
index d075549..e85d5cc 100644 (file)
@@ -225,6 +225,7 @@ reread:
                goto reread;
        }
 
+       mp->m_features |= xfs_sb_version_to_features(sbp);
        xfs_reinit_percpu_counters(mp);
 
        /* no need to be quiet anymore, so reset the buf ops */
@@ -318,7 +319,7 @@ xfs_validate_new_dalign(
                }
        }
 
-       if (!xfs_sb_version_hasdalign(&mp->m_sb)) {
+       if (!xfs_has_dalign(mp)) {
                xfs_warn(mp,
 "cannot change alignment: superblock does not support data alignment");
                return -EINVAL;
@@ -350,7 +351,7 @@ xfs_update_alignment(
                sbp->sb_width = mp->m_swidth;
                mp->m_update_sb = true;
        } else if ((mp->m_flags & XFS_MOUNT_NOALIGN) != XFS_MOUNT_NOALIGN &&
-                   xfs_sb_version_hasdalign(&mp->m_sb)) {
+                   xfs_has_dalign(mp)) {
                mp->m_dalign = sbp->sb_unit;
                mp->m_swidth = sbp->sb_width;
        }
@@ -365,13 +366,16 @@ void
 xfs_set_low_space_thresholds(
        struct xfs_mount        *mp)
 {
-       int i;
+       uint64_t                dblocks = mp->m_sb.sb_dblocks;
+       uint64_t                rtexts = mp->m_sb.sb_rextents;
+       int                     i;
 
-       for (i = 0; i < XFS_LOWSP_MAX; i++) {
-               uint64_t space = mp->m_sb.sb_dblocks;
+       do_div(dblocks, 100);
+       do_div(rtexts, 100);
 
-               do_div(space, 100);
-               mp->m_low_space[i] = space * (i + 1);
+       for (i = 0; i < XFS_LOWSP_MAX; i++) {
+               mp->m_low_space[i] = dblocks * (i + 1);
+               mp->m_low_rtexts[i] = rtexts * (i + 1);
        }
 }
 
@@ -502,7 +506,7 @@ xfs_check_summary_counts(
         * superblock to be correct and we don't need to do anything here.
         * Otherwise, recalculate the summary counters.
         */
-       if ((!xfs_sb_version_haslazysbcount(&mp->m_sb) ||
+       if ((!xfs_has_lazysbcount(mp) ||
             XFS_LAST_UNMOUNT_WAS_CLEAN(mp)) &&
            !xfs_fs_has_sickness(mp, XFS_SICK_FS_COUNTERS))
                return 0;
@@ -514,7 +518,8 @@ xfs_check_summary_counts(
  * Flush and reclaim dirty inodes in preparation for unmount. Inodes and
  * internal inode structures can be sitting in the CIL and AIL at this point,
  * so we need to unpin them, write them back and/or reclaim them before unmount
- * can proceed.
+ * can proceed.  In other words, callers are required to have inactivated all
+ * inodes.
  *
  * An inode cluster that has been freed can have its buffer still pinned in
  * memory because the transaction is still sitting in a iclog. The stale inodes
@@ -546,6 +551,7 @@ xfs_unmount_flush_inodes(
        mp->m_flags |= XFS_MOUNT_UNMOUNTING;
 
        xfs_ail_push_all_sync(mp->m_ail);
+       xfs_inodegc_stop(mp);
        cancel_delayed_work_sync(&mp->m_reclaim_work);
        xfs_reclaim_inodes(mp);
        xfs_health_unmount(mp);
@@ -607,29 +613,13 @@ xfs_mountfs(
                xfs_warn(mp, "correcting sb_features alignment problem");
                sbp->sb_features2 |= sbp->sb_bad_features2;
                mp->m_update_sb = true;
-
-               /*
-                * Re-check for ATTR2 in case it was found in bad_features2
-                * slot.
-                */
-               if (xfs_sb_version_hasattr2(&mp->m_sb) &&
-                  !(mp->m_flags & XFS_MOUNT_NOATTR2))
-                       mp->m_flags |= XFS_MOUNT_ATTR2;
        }
 
-       if (xfs_sb_version_hasattr2(&mp->m_sb) &&
-          (mp->m_flags & XFS_MOUNT_NOATTR2)) {
-               xfs_sb_version_removeattr2(&mp->m_sb);
-               mp->m_update_sb = true;
-
-               /* update sb_versionnum for the clearing of the morebits */
-               if (!sbp->sb_features2)
-                       mp->m_update_sb = true;
-       }
 
        /* always use v2 inodes by default now */
        if (!(mp->m_sb.sb_versionnum & XFS_SB_VERSION_NLINKBIT)) {
                mp->m_sb.sb_versionnum |= XFS_SB_VERSION_NLINKBIT;
+               mp->m_features |= XFS_FEAT_NLINK;
                mp->m_update_sb = true;
        }
 
@@ -702,7 +692,7 @@ xfs_mountfs(
         * cluster size. Full inode chunk alignment must match the chunk size,
         * but that is checked on sb read verification...
         */
-       if (xfs_sb_version_hassparseinodes(&mp->m_sb) &&
+       if (xfs_has_sparseinodes(mp) &&
            mp->m_sb.sb_spino_align !=
                        XFS_B_TO_FSBT(mp, igeo->inode_cluster_size_raw)) {
                xfs_warn(mp,
@@ -764,6 +754,10 @@ xfs_mountfs(
                goto out_free_perag;
        }
 
+       error = xfs_inodegc_register_shrinker(mp);
+       if (error)
+               goto out_fail_wait;
+
        /*
         * Log's mount-time initialization. The first part of recovery can place
         * some items on the AIL, to be handled when recovery is finished or
@@ -774,7 +768,7 @@ xfs_mountfs(
                              XFS_FSB_TO_BB(mp, sbp->sb_logblocks));
        if (error) {
                xfs_warn(mp, "log mount failed");
-               goto out_fail_wait;
+               goto out_inodegc_shrinker;
        }
 
        /* Make sure the summary counts are ok. */
@@ -782,6 +776,20 @@ xfs_mountfs(
        if (error)
                goto out_log_dealloc;
 
+       /* Enable background inode inactivation workers. */
+       xfs_inodegc_start(mp);
+       xfs_blockgc_start(mp);
+
+       /*
+        * Now that we've recovered any pending superblock feature bit
+        * additions, we can finish setting up the attr2 behaviour for the
+        * mount. If no attr2 mount options were specified, the we use the
+        * behaviour specified by the superblock feature bit.
+        */
+       if (!(mp->m_flags & (XFS_MOUNT_ATTR2|XFS_MOUNT_NOATTR2)) &&
+           xfs_has_attr2(mp))
+               mp->m_flags |= XFS_MOUNT_ATTR2;
+
        /*
         * Get and sanity-check the root inode.
         * Save the pointer to it in the mount structure.
@@ -836,13 +844,11 @@ xfs_mountfs(
        /*
         * Initialise the XFS quota management subsystem for this mount
         */
-       if (XFS_IS_QUOTA_RUNNING(mp)) {
+       if (XFS_IS_QUOTA_ON(mp)) {
                error = xfs_qm_newmount(mp, &quotamount, &quotaflags);
                if (error)
                        goto out_rtunmount;
        } else {
-               ASSERT(!XFS_IS_QUOTA_ON(mp));
-
                /*
                 * If a file system had quotas running earlier, but decided to
                 * mount without -o uquota/pquota/gquota options, revoke the
@@ -944,6 +950,15 @@ xfs_mountfs(
        xfs_irele(rip);
        /* Clean out dquots that might be in memory after quotacheck. */
        xfs_qm_unmount(mp);
+
+       /*
+        * Inactivate all inodes that might still be in memory after a log
+        * intent recovery failure so that reclaim can free them.  Metadata
+        * inodes and the root directory shouldn't need inactivation, but the
+        * mount failed for some reason, so pull down all the state and flee.
+        */
+       xfs_inodegc_flush(mp);
+
        /*
         * Flush all inode reclamation work and flush the log.
         * We have to do this /after/ rtunmount and qm_unmount because those
@@ -958,6 +973,8 @@ xfs_mountfs(
        xfs_unmount_flush_inodes(mp);
  out_log_dealloc:
        xfs_log_mount_cancel(mp);
+ out_inodegc_shrinker:
+       unregister_shrinker(&mp->m_inodegc_shrinker);
  out_fail_wait:
        if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)
                xfs_buftarg_drain(mp->m_logdev_targp);
@@ -991,6 +1008,16 @@ xfs_unmountfs(
        uint64_t                resblks;
        int                     error;
 
+       /*
+        * Perform all on-disk metadata updates required to inactivate inodes
+        * that the VFS evicted earlier in the unmount process.  Freeing inodes
+        * and discarding CoW fork preallocations can cause shape changes to
+        * the free inode and refcount btrees, respectively, so we must finish
+        * this before we discard the metadata space reservations.  Metadata
+        * inodes and the root directory do not require inactivation.
+        */
+       xfs_inodegc_flush(mp);
+
        xfs_blockgc_stop(mp);
        xfs_fs_unreserve_ag_blocks(mp);
        xfs_qm_unmount_quotas(mp);
@@ -1028,6 +1055,7 @@ xfs_unmountfs(
 #if defined(DEBUG)
        xfs_errortag_clearall(mp);
 #endif
+       unregister_shrinker(&mp->m_inodegc_shrinker);
        xfs_free_perag(mp);
 
        xfs_errortag_del(mp);
@@ -1055,14 +1083,6 @@ xfs_fs_writable(
        return true;
 }
 
-/*
- * Deltas for the block count can vary from 1 to very large, but lock contention
- * only occurs on frequent small block count updates such as in the delayed
- * allocation path for buffered writes (page a time updates). Hence we set
- * a large batch count (1024) to minimise global counter updates except when
- * we get near to ENOSPC and we have to be very accurate with our updates.
- */
-#define XFS_FDBLOCKS_BATCH     1024
 int
 xfs_mod_fdblocks(
        struct xfs_mount        *mp,
@@ -1210,12 +1230,122 @@ void
 xfs_force_summary_recalc(
        struct xfs_mount        *mp)
 {
-       if (!xfs_sb_version_haslazysbcount(&mp->m_sb))
+       if (!xfs_has_lazysbcount(mp))
                return;
 
        xfs_fs_mark_sick(mp, XFS_SICK_FS_COUNTERS);
 }
 
+/*
+ * Enable a log incompat feature flag in the primary superblock.  The caller
+ * cannot have any other transactions in progress.
+ */
+int
+xfs_add_incompat_log_feature(
+       struct xfs_mount        *mp,
+       uint32_t                feature)
+{
+       struct xfs_dsb          *dsb;
+       int                     error;
+
+       ASSERT(hweight32(feature) == 1);
+       ASSERT(!(feature & XFS_SB_FEAT_INCOMPAT_LOG_UNKNOWN));
+
+       /*
+        * Force the log to disk and kick the background AIL thread to reduce
+        * the chances that the bwrite will stall waiting for the AIL to unpin
+        * the primary superblock buffer.  This isn't a data integrity
+        * operation, so we don't need a synchronous push.
+        */
+       error = xfs_log_force(mp, XFS_LOG_SYNC);
+       if (error)
+               return error;
+       xfs_ail_push_all(mp->m_ail);
+
+       /*
+        * Lock the primary superblock buffer to serialize all callers that
+        * are trying to set feature bits.
+        */
+       xfs_buf_lock(mp->m_sb_bp);
+       xfs_buf_hold(mp->m_sb_bp);
+
+       if (XFS_FORCED_SHUTDOWN(mp)) {
+               error = -EIO;
+               goto rele;
+       }
+
+       if (xfs_sb_has_incompat_log_feature(&mp->m_sb, feature))
+               goto rele;
+
+       /*
+        * Write the primary superblock to disk immediately, because we need
+        * the log_incompat bit to be set in the primary super now to protect
+        * the log items that we're going to commit later.
+        */
+       dsb = mp->m_sb_bp->b_addr;
+       xfs_sb_to_disk(dsb, &mp->m_sb);
+       dsb->sb_features_log_incompat |= cpu_to_be32(feature);
+       error = xfs_bwrite(mp->m_sb_bp);
+       if (error)
+               goto shutdown;
+
+       /*
+        * Add the feature bits to the incore superblock before we unlock the
+        * buffer.
+        */
+       xfs_sb_add_incompat_log_features(&mp->m_sb, feature);
+       xfs_buf_relse(mp->m_sb_bp);
+
+       /* Log the superblock to disk. */
+       return xfs_sync_sb(mp, false);
+shutdown:
+       xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
+rele:
+       xfs_buf_relse(mp->m_sb_bp);
+       return error;
+}
+
+/*
+ * Clear all the log incompat flags from the superblock.
+ *
+ * The caller cannot be in a transaction, must ensure that the log does not
+ * contain any log items protected by any log incompat bit, and must ensure
+ * that there are no other threads that depend on the state of the log incompat
+ * feature flags in the primary super.
+ *
+ * Returns true if the superblock is dirty.
+ */
+bool
+xfs_clear_incompat_log_features(
+       struct xfs_mount        *mp)
+{
+       bool                    ret = false;
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb) ||
+           !xfs_sb_has_incompat_log_feature(&mp->m_sb,
+                               XFS_SB_FEAT_INCOMPAT_LOG_ALL) ||
+           XFS_FORCED_SHUTDOWN(mp))
+               return false;
+
+       /*
+        * Update the incore superblock.  We synchronize on the primary super
+        * buffer lock to be consistent with the add function, though at least
+        * in theory this shouldn't be necessary.
+        */
+       xfs_buf_lock(mp->m_sb_bp);
+       xfs_buf_hold(mp->m_sb_bp);
+
+       if (xfs_sb_has_incompat_log_feature(&mp->m_sb,
+                               XFS_SB_FEAT_INCOMPAT_LOG_ALL)) {
+               xfs_info(mp, "Clearing log incompat feature flags.");
+               xfs_sb_remove_incompat_log_features(&mp->m_sb);
+               ret = true;
+       }
+
+       xfs_buf_relse(mp->m_sb_bp);
+       return ret;
+}
+
 /*
  * Update the in-core delayed block counter.
  *