Merge branch 'sfc-3.12' of git://git.kernel.org/pub/scm/linux/kernel/git/bwh/sfc
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / xfs / xfs_log.c
index d852a2b..5372d58 100644 (file)
@@ -614,7 +614,8 @@ xfs_log_mount(
        xfs_daddr_t     blk_offset,
        int             num_bblks)
 {
-       int             error;
+       int             error = 0;
+       int             min_logfsbs;
 
        if (!(mp->m_flags & XFS_MOUNT_NORECOVERY))
                xfs_notice(mp, "Mounting Filesystem");
@@ -631,6 +632,50 @@ xfs_log_mount(
        }
 
        /*
+        * Validate the given log space and drop a critical message via syslog
+        * if the log size is too small that would lead to some unexpected
+        * situations in transaction log space reservation stage.
+        *
+        * Note: we can't just reject the mount if the validation fails.  This
+        * would mean that people would have to downgrade their kernel just to
+        * remedy the situation as there is no way to grow the log (short of
+        * black magic surgery with xfs_db).
+        *
+        * We can, however, reject mounts for CRC format filesystems, as the
+        * mkfs binary being used to make the filesystem should never create a
+        * filesystem with a log that is too small.
+        */
+       min_logfsbs = xfs_log_calc_minimum_size(mp);
+
+       if (mp->m_sb.sb_logblocks < min_logfsbs) {
+               xfs_warn(mp,
+               "Log size %d blocks too small, minimum size is %d blocks",
+                        mp->m_sb.sb_logblocks, min_logfsbs);
+               error = EINVAL;
+       } else if (mp->m_sb.sb_logblocks > XFS_MAX_LOG_BLOCKS) {
+               xfs_warn(mp,
+               "Log size %d blocks too large, maximum size is %lld blocks",
+                        mp->m_sb.sb_logblocks, XFS_MAX_LOG_BLOCKS);
+               error = EINVAL;
+       } else if (XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks) > XFS_MAX_LOG_BYTES) {
+               xfs_warn(mp,
+               "log size %lld bytes too large, maximum size is %lld bytes",
+                        XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks),
+                        XFS_MAX_LOG_BYTES);
+               error = EINVAL;
+       }
+       if (error) {
+               if (xfs_sb_version_hascrc(&mp->m_sb)) {
+                       xfs_crit(mp, "AAIEEE! Log failed size checks. Abort!");
+                       ASSERT(0);
+                       goto out_free_log;
+               }
+               xfs_crit(mp,
+"Log size out of supported range. Continuing onwards, but if log hangs are\n"
+"experienced then please report this message in the bug report.");
+       }
+
+       /*
         * Initialize the AIL now we have a log.
         */
        error = xfs_trans_ail_init(mp);
@@ -720,7 +765,7 @@ xfs_log_mount_finish(xfs_mount_t *mp)
  * Unmount record used to have a string "Unmount filesystem--" in the
  * data section where the "Un" was really a magic number (XLOG_UNMOUNT_TYPE).
  * We just write the magic number now since that particular field isn't
- * currently architecture converted and "nUmount" is a bit foo.
+ * currently architecture converted and "Unmount" is a bit foo.
  * As far as I know, there weren't any dependencies on the old behaviour.
  */
 
@@ -1941,7 +1986,7 @@ xlog_print_tic_res(
 
        xfs_alert_tag(mp, XFS_PTAG_LOGRES,
                "xlog_write: reservation ran out. Need to up reservation");
-       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+       xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR);
 }
 
 /*
@@ -2044,7 +2089,7 @@ xlog_write_setup_ophdr(
  * Set up the parameters of the region copy into the log. This has
  * to handle region write split across multiple log buffers - this
  * state is kept external to this function so that this code can
- * can be written in an obvious, self documenting manner.
+ * be written in an obvious, self documenting manner.
  */
 static int
 xlog_write_setup_copy(
@@ -3391,24 +3436,17 @@ xfs_log_ticket_get(
 }
 
 /*
- * Allocate and initialise a new log ticket.
+ * Figure out the total log space unit (in bytes) that would be
+ * required for a log ticket.
  */
-struct xlog_ticket *
-xlog_ticket_alloc(
-       struct xlog     *log,
-       int             unit_bytes,
-       int             cnt,
-       char            client,
-       bool            permanent,
-       xfs_km_flags_t  alloc_flags)
+int
+xfs_log_calc_unit_res(
+       struct xfs_mount        *mp,
+       int                     unit_bytes)
 {
-       struct xlog_ticket *tic;
-       uint            num_headers;
-       int             iclog_space;
-
-       tic = kmem_zone_zalloc(xfs_log_ticket_zone, alloc_flags);
-       if (!tic)
-               return NULL;
+       struct xlog             *log = mp->m_log;
+       int                     iclog_space;
+       uint                    num_headers;
 
        /*
         * Permanent reservations have up to 'cnt'-1 active log operations
@@ -3483,20 +3521,43 @@ xlog_ticket_alloc(
        unit_bytes += log->l_iclog_hsize;
 
        /* for roundoff padding for transaction data and one for commit record */
-       if (xfs_sb_version_haslogv2(&log->l_mp->m_sb) &&
-           log->l_mp->m_sb.sb_logsunit > 1) {
+       if (xfs_sb_version_haslogv2(&mp->m_sb) && mp->m_sb.sb_logsunit > 1) {
                /* log su roundoff */
-               unit_bytes += 2*log->l_mp->m_sb.sb_logsunit;
+               unit_bytes += 2 * mp->m_sb.sb_logsunit;
        } else {
                /* BB roundoff */
-               unit_bytes += 2*BBSIZE;
+               unit_bytes += 2 * BBSIZE;
         }
 
+       return unit_bytes;
+}
+
+/*
+ * Allocate and initialise a new log ticket.
+ */
+struct xlog_ticket *
+xlog_ticket_alloc(
+       struct xlog             *log,
+       int                     unit_bytes,
+       int                     cnt,
+       char                    client,
+       bool                    permanent,
+       xfs_km_flags_t          alloc_flags)
+{
+       struct xlog_ticket      *tic;
+       int                     unit_res;
+
+       tic = kmem_zone_zalloc(xfs_log_ticket_zone, alloc_flags);
+       if (!tic)
+               return NULL;
+
+       unit_res = xfs_log_calc_unit_res(log->l_mp, unit_bytes);
+
        atomic_set(&tic->t_ref, 1);
        tic->t_task             = current;
        INIT_LIST_HEAD(&tic->t_queue);
-       tic->t_unit_res         = unit_bytes;
-       tic->t_curr_res         = unit_bytes;
+       tic->t_unit_res         = unit_res;
+       tic->t_curr_res         = unit_res;
        tic->t_cnt              = cnt;
        tic->t_ocnt             = cnt;
        tic->t_tid              = prandom_u32();