Merge branch 'for-linus-3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[firefly-linux-kernel-4.4.55.git] / fs / xfs / xfs_log.c
index aaadee0969c929022725b11dcc2cd3a66995bd29..f52c72a1a06f28c8438dc9248040fe6682ad6843 100644 (file)
@@ -268,7 +268,7 @@ xlog_grant_head_wait(
                __set_current_state(TASK_UNINTERRUPTIBLE);
                spin_unlock(&head->lock);
 
-               XFS_STATS_INC(xs_sleep_logspace);
+               XFS_STATS_INC(log->l_mp, xs_sleep_logspace);
 
                trace_xfs_log_grant_sleep(log, tic);
                schedule();
@@ -379,7 +379,7 @@ xfs_log_regrant(
        if (XLOG_FORCED_SHUTDOWN(log))
                return -EIO;
 
-       XFS_STATS_INC(xs_try_logspace);
+       XFS_STATS_INC(mp, xs_try_logspace);
 
        /*
         * This is a new transaction on the ticket, so we need to change the
@@ -448,7 +448,7 @@ xfs_log_reserve(
        if (XLOG_FORCED_SHUTDOWN(log))
                return -EIO;
 
-       XFS_STATS_INC(xs_try_logspace);
+       XFS_STATS_INC(mp, xs_try_logspace);
 
        ASSERT(*ticp == NULL);
        tic = xlog_ticket_alloc(log, unit_bytes, cnt, client, permanent,
@@ -1768,7 +1768,7 @@ xlog_sync(
        int             v2 = xfs_sb_version_haslogv2(&log->l_mp->m_sb);
        int             size;
 
-       XFS_STATS_INC(xs_log_writes);
+       XFS_STATS_INC(log->l_mp, xs_log_writes);
        ASSERT(atomic_read(&iclog->ic_refcnt) == 0);
 
        /* Add for LR header */
@@ -1805,7 +1805,7 @@ xlog_sync(
        bp = iclog->ic_bp;
        XFS_BUF_SET_ADDR(bp, BLOCK_LSN(be64_to_cpu(iclog->ic_header.h_lsn)));
 
-       XFS_STATS_ADD(xs_log_blocks, BTOBB(count));
+       XFS_STATS_ADD(log->l_mp, xs_log_blocks, BTOBB(count));
 
        /* Do we need to split this write into 2 parts? */
        if (XFS_BUF_ADDR(bp) + BTOBB(count) > log->l_logBBsize) {
@@ -2422,11 +2422,20 @@ xlog_write(
                                                     &partial_copy_len);
                        xlog_verify_dest_ptr(log, ptr);
 
-                       /* copy region */
+                       /*
+                        * Copy region.
+                        *
+                        * Unmount records just log an opheader, so can have
+                        * empty payloads with no data region to copy. Hence we
+                        * only copy the payload if the vector says it has data
+                        * to copy.
+                        */
                        ASSERT(copy_len >= 0);
-                       memcpy(ptr, reg->i_addr + copy_off, copy_len);
-                       xlog_write_adv_cnt(&ptr, &len, &log_offset, copy_len);
-
+                       if (copy_len > 0) {
+                               memcpy(ptr, reg->i_addr + copy_off, copy_len);
+                               xlog_write_adv_cnt(&ptr, &len, &log_offset,
+                                                  copy_len);
+                       }
                        copy_len += start_rec_copy + sizeof(xlog_op_header_t);
                        record_cnt++;
                        data_cnt += contwr ? copy_len : 0;
@@ -2913,7 +2922,7 @@ restart:
 
        iclog = log->l_iclog;
        if (iclog->ic_state != XLOG_STATE_ACTIVE) {
-               XFS_STATS_INC(xs_log_noiclogs);
+               XFS_STATS_INC(log->l_mp, xs_log_noiclogs);
 
                /* Wait for log writes to have flushed */
                xlog_wait(&log->l_flush_wait, &log->l_icloglock);
@@ -3165,11 +3174,19 @@ xlog_state_switch_iclogs(
        }
 
        if (log->l_curr_block >= log->l_logBBsize) {
+               /*
+                * Rewind the current block before the cycle is bumped to make
+                * sure that the combined LSN never transiently moves forward
+                * when the log wraps to the next cycle. This is to support the
+                * unlocked sample of these fields from xlog_valid_lsn(). Most
+                * other cases should acquire l_icloglock.
+                */
+               log->l_curr_block -= log->l_logBBsize;
+               ASSERT(log->l_curr_block >= 0);
+               smp_wmb();
                log->l_curr_cycle++;
                if (log->l_curr_cycle == XLOG_HEADER_MAGIC_NUM)
                        log->l_curr_cycle++;
-               log->l_curr_block -= log->l_logBBsize;
-               ASSERT(log->l_curr_block >= 0);
        }
        ASSERT(iclog == log->l_iclog);
        log->l_iclog = iclog->ic_next;
@@ -3212,7 +3229,7 @@ _xfs_log_force(
        struct xlog_in_core     *iclog;
        xfs_lsn_t               lsn;
 
-       XFS_STATS_INC(xs_log_force);
+       XFS_STATS_INC(mp, xs_log_force);
 
        xlog_cil_force(log);
 
@@ -3297,7 +3314,7 @@ maybe_sleep:
                        spin_unlock(&log->l_icloglock);
                        return -EIO;
                }
-               XFS_STATS_INC(xs_log_force_sleep);
+               XFS_STATS_INC(mp, xs_log_force_sleep);
                xlog_wait(&iclog->ic_force_wait, &log->l_icloglock);
                /*
                 * No need to grab the log lock here since we're
@@ -3362,7 +3379,7 @@ _xfs_log_force_lsn(
 
        ASSERT(lsn != 0);
 
-       XFS_STATS_INC(xs_log_force);
+       XFS_STATS_INC(mp, xs_log_force);
 
        lsn = xlog_cil_force_lsn(log, lsn);
        if (lsn == NULLCOMMITLSN)
@@ -3411,7 +3428,7 @@ try_again:
                             (XLOG_STATE_WANT_SYNC | XLOG_STATE_SYNCING))) {
                                ASSERT(!(iclog->ic_state & XLOG_STATE_IOERROR));
 
-                               XFS_STATS_INC(xs_log_force_sleep);
+                               XFS_STATS_INC(mp, xs_log_force_sleep);
 
                                xlog_wait(&iclog->ic_prev->ic_write_wait,
                                                        &log->l_icloglock);
@@ -3441,7 +3458,7 @@ try_again:
                                spin_unlock(&log->l_icloglock);
                                return -EIO;
                        }
-                       XFS_STATS_INC(xs_log_force_sleep);
+                       XFS_STATS_INC(mp, xs_log_force_sleep);
                        xlog_wait(&iclog->ic_force_wait, &log->l_icloglock);
                        /*
                         * No need to grab the log lock here since we're
@@ -4023,3 +4040,45 @@ xlog_iclogs_empty(
        return 1;
 }
 
+/*
+ * Verify that an LSN stamped into a piece of metadata is valid. This is
+ * intended for use in read verifiers on v5 superblocks.
+ */
+bool
+xfs_log_check_lsn(
+       struct xfs_mount        *mp,
+       xfs_lsn_t               lsn)
+{
+       struct xlog             *log = mp->m_log;
+       bool                    valid;
+
+       /*
+        * norecovery mode skips mount-time log processing and unconditionally
+        * resets the in-core LSN. We can't validate in this mode, but
+        * modifications are not allowed anyways so just return true.
+        */
+       if (mp->m_flags & XFS_MOUNT_NORECOVERY)
+               return true;
+
+       /*
+        * Some metadata LSNs are initialized to NULL (e.g., the agfl). This is
+        * handled by recovery and thus safe to ignore here.
+        */
+       if (lsn == NULLCOMMITLSN)
+               return true;
+
+       valid = xlog_valid_lsn(mp->m_log, lsn);
+
+       /* warn the user about what's gone wrong before verifier failure */
+       if (!valid) {
+               spin_lock(&log->l_icloglock);
+               xfs_warn(mp,
+"Corruption warning: Metadata has LSN (%d:%d) ahead of current LSN (%d:%d). "
+"Please unmount and run xfs_repair (>= v4.3) to resolve.",
+                        CYCLE_LSN(lsn), BLOCK_LSN(lsn),
+                        log->l_curr_cycle, log->l_curr_block);
+               spin_unlock(&log->l_icloglock);
+       }
+
+       return valid;
+}