Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
authorAlex Elder <aelder@sgi.com>
Mon, 17 Oct 2011 20:42:02 +0000 (15:42 -0500)
committerAlex Elder <aelder@sgi.com>
Mon, 17 Oct 2011 20:42:02 +0000 (15:42 -0500)
Resolved conflicts:
  fs/xfs/xfs_trans_priv.h:
    - deleted struct xfs_ail field xa_flags
    - kept field xa_log_flush in struct xfs_ail
  fs/xfs/xfs_trans_ail.c:
    - in xfsaild_push(), in XFS_ITEM_PUSHBUF case, replaced
      "flush_log = 1" with "ailp->xa_log_flush++"

Signed-off-by: Alex Elder <aelder@sgi.com>
1  2 
fs/xfs/xfs_buf_item.c
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_super.c
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_ail.c
fs/xfs/xfs_trans_priv.h

diff --combined fs/xfs/xfs_buf_item.c
@@@ -629,7 -629,7 +629,7 @@@ xfs_buf_item_push
   * the xfsbufd to get this buffer written. We have to unlock the buffer
   * to allow the xfsbufd to write it, too.
   */
- STATIC void
+ STATIC bool
  xfs_buf_item_pushbuf(
        struct xfs_log_item     *lip)
  {
  
        xfs_buf_delwri_promote(bp);
        xfs_buf_relse(bp);
+       return true;
  }
  
  STATIC void
@@@ -966,8 -967,7 +967,8 @@@ xfs_buf_iodone_callbacks
         * I/O errors, there's no point in giving this a retry.
         */
        if (XFS_FORCED_SHUTDOWN(mp)) {
 -              XFS_BUF_SUPER_STALE(bp);
 +              xfs_buf_stale(bp);
 +              XFS_BUF_DONE(bp);
                trace_xfs_buf_item_iodone(bp, _RET_IP_);
                goto do_callbacks;
        }
        if (bp->b_target != lasttarg ||
            time_after(jiffies, (lasttime + 5*HZ))) {
                lasttime = jiffies;
 -              xfs_alert(mp, "Device %s: metadata write error block 0x%llx",
 -                      xfs_buf_target_name(bp->b_target),
 -                    (__uint64_t)XFS_BUF_ADDR(bp));
 +              xfs_buf_ioerror_alert(bp, __func__);
        }
        lasttarg = bp->b_target;
  
                xfs_buf_ioerror(bp, 0); /* errno of 0 unsets the flag */
  
                if (!XFS_BUF_ISSTALE(bp)) {
 -                      XFS_BUF_DELAYWRITE(bp);
 +                      xfs_buf_delwri_queue(bp);
                        XFS_BUF_DONE(bp);
                }
                ASSERT(bp->b_iodone != NULL);
         * If the write of the buffer was synchronous, we want to make
         * sure to return the error to the caller of xfs_bwrite().
         */
 -      XFS_BUF_STALE(bp);
 +      xfs_buf_stale(bp);
        XFS_BUF_DONE(bp);
 -      XFS_BUF_UNDELAYWRITE(bp);
  
        trace_xfs_buf_error_relse(bp, _RET_IP_);
  
diff --combined fs/xfs/xfs_inode_item.c
@@@ -658,8 -658,10 +658,8 @@@ xfs_inode_item_unlock
  
        lock_flags = iip->ili_lock_flags;
        iip->ili_lock_flags = 0;
 -      if (lock_flags) {
 +      if (lock_flags)
                xfs_iunlock(ip, lock_flags);
 -              IRELE(ip);
 -      }
  }
  
  /*
@@@ -706,13 -708,14 +706,14 @@@ xfs_inode_item_committed
   * marked delayed write. If that's the case, we'll promote it and that will
   * allow the caller to write the buffer by triggering the xfsbufd to run.
   */
- STATIC void
+ STATIC bool
  xfs_inode_item_pushbuf(
        struct xfs_log_item     *lip)
  {
        struct xfs_inode_log_item *iip = INODE_ITEM(lip);
        struct xfs_inode        *ip = iip->ili_inode;
        struct xfs_buf          *bp;
+       bool                    ret = true;
  
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED));
  
        if (completion_done(&ip->i_flush) ||
            !(lip->li_flags & XFS_LI_IN_AIL)) {
                xfs_iunlock(ip, XFS_ILOCK_SHARED);
-               return;
+               return true;
        }
  
        bp = xfs_incore(ip->i_mount->m_ddev_targp, iip->ili_format.ilf_blkno,
  
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
        if (!bp)
-               return;
+               return true;
        if (XFS_BUF_ISDELAYWRITE(bp))
                xfs_buf_delwri_promote(bp);
+       if (xfs_buf_ispinned(bp))
+               ret = false;
        xfs_buf_relse(bp);
+       return ret;
  }
  
  /*
diff --combined fs/xfs/xfs_super.c
@@@ -796,6 -796,8 +796,6 @@@ xfs_fs_destroy_inode
        if (is_bad_inode(inode))
                goto out_reclaim;
  
 -      xfs_ioend_wait(ip);
 -
        ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
  
        /*
@@@ -835,6 -837,7 +835,6 @@@ xfs_fs_inode_init_once
        inode_init_once(VFS_I(ip));
  
        /* xfs inode */
 -      atomic_set(&ip->i_iocount, 0);
        atomic_set(&ip->i_pincount, 0);
        spin_lock_init(&ip->i_flags_lock);
        init_waitqueue_head(&ip->i_ipin_wait);
@@@ -884,7 -887,7 +884,7 @@@ xfs_log_inode
        }
  
        xfs_ilock(ip, XFS_ILOCK_EXCL);
 -      xfs_trans_ijoin_ref(tp, ip, XFS_ILOCK_EXCL);
 +      xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
        xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
        return xfs_trans_commit(tp, 0);
  }
@@@ -911,8 -914,9 +911,8 @@@ xfs_fs_write_inode
                 * of forcing it all the way to stable storage using a
                 * synchronous transaction we let the log force inside the
                 * ->sync_fs call do that for thus, which reduces the number
 -               * of synchronous log foces dramatically.
 +               * of synchronous log forces dramatically.
                 */
 -              xfs_ioend_wait(ip);
                error = xfs_log_inode(ip);
                if (error)
                        goto out;
@@@ -1015,7 -1019,7 +1015,7 @@@ xfs_fs_put_super
         */
        xfs_filestream_unmount(mp);
  
 -      XFS_bflush(mp->m_ddev_targp);
 +      xfs_flush_buftarg(mp->m_ddev_targp, 1);
  
        xfs_unmountfs(mp);
        xfs_freesb(mp);
@@@ -1439,7 -1443,7 +1439,7 @@@ xfs_fs_fill_super
         */
        xfs_filestream_unmount(mp);
  
 -      XFS_bflush(mp->m_ddev_targp);
 +      xfs_flush_buftarg(mp->m_ddev_targp, 1);
  
        xfs_unmountfs(mp);
        goto out_free_sb;
@@@ -1648,24 -1652,13 +1648,13 @@@ xfs_init_workqueues(void
         */
        xfs_syncd_wq = alloc_workqueue("xfssyncd", WQ_CPU_INTENSIVE, 8);
        if (!xfs_syncd_wq)
-               goto out;
-       xfs_ail_wq = alloc_workqueue("xfsail", WQ_CPU_INTENSIVE, 8);
-       if (!xfs_ail_wq)
-               goto out_destroy_syncd;
+               return -ENOMEM;
        return 0;
- out_destroy_syncd:
-       destroy_workqueue(xfs_syncd_wq);
- out:
-       return -ENOMEM;
  }
  
  STATIC void
  xfs_destroy_workqueues(void)
  {
-       destroy_workqueue(xfs_ail_wq);
        destroy_workqueue(xfs_syncd_wq);
  }
  
@@@ -1677,6 -1670,7 +1666,6 @@@ init_xfs_fs(void
        printk(KERN_INFO XFS_VERSION_STRING " with "
                         XFS_BUILD_OPTIONS " enabled\n");
  
 -      xfs_ioend_init();
        xfs_dir_startup();
  
        error = xfs_init_zones();
diff --combined fs/xfs/xfs_trans.h
@@@ -350,7 -350,7 +350,7 @@@ typedef struct xfs_item_ops 
        void (*iop_unlock)(xfs_log_item_t *);
        xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
        void (*iop_push)(xfs_log_item_t *);
-       void (*iop_pushbuf)(xfs_log_item_t *);
+       bool (*iop_pushbuf)(xfs_log_item_t *);
        void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
  } xfs_item_ops_t;
  
@@@ -470,7 -470,8 +470,7 @@@ void               xfs_trans_stale_inode_buf(xfs_tra
  void          xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint);
  void          xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *);
  void          xfs_trans_ichgtime(struct xfs_trans *, struct xfs_inode *, int);
 -void          xfs_trans_ijoin_ref(struct xfs_trans *, struct xfs_inode *, uint);
 -void          xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *);
 +void          xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *, uint);
  void          xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint);
  void          xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
  struct xfs_efi_log_item       *xfs_trans_get_efi(xfs_trans_t *, uint);
@@@ -486,7 -487,10 +486,7 @@@ void              xfs_trans_log_efd_extent(xfs_tran
                                         struct xfs_efd_log_item *,
                                         xfs_fsblock_t,
                                         xfs_extlen_t);
 -int           _xfs_trans_commit(xfs_trans_t *,
 -                                uint flags,
 -                                int *);
 -#define xfs_trans_commit(tp, flags)   _xfs_trans_commit(tp, flags, NULL)
 +int           xfs_trans_commit(xfs_trans_t *, uint flags);
  void          xfs_trans_cancel(xfs_trans_t *, int);
  int           xfs_trans_ail_init(struct xfs_mount *);
  void          xfs_trans_ail_destroy(struct xfs_mount *);
diff --combined fs/xfs/xfs_trans_ail.c
@@@ -28,8 -28,6 +28,6 @@@
  #include "xfs_trans_priv.h"
  #include "xfs_error.h"
  
- struct workqueue_struct       *xfs_ail_wq;    /* AIL workqueue */
  #ifdef DEBUG
  /*
   * Check that the list is sorted as it should be.
@@@ -356,40 -354,22 +354,34 @@@ xfs_ail_delete
        xfs_trans_ail_cursor_clear(ailp, lip);
  }
  
- /*
-  * xfs_ail_worker does the work of pushing on the AIL. It will requeue itself
-  * to run at a later time if there is more work to do to complete the push.
-  */
- STATIC void
- xfs_ail_worker(
-       struct work_struct      *work)
+ static long
+ xfsaild_push(
+       struct xfs_ail          *ailp)
  {
-       struct xfs_ail          *ailp = container_of(to_delayed_work(work),
-                                       struct xfs_ail, xa_work);
        xfs_mount_t             *mp = ailp->xa_mount;
        struct xfs_ail_cursor   cur;
        xfs_log_item_t          *lip;
        xfs_lsn_t               lsn;
        xfs_lsn_t               target;
        long                    tout = 10;
 -      int                     flush_log = 0;
        int                     stuck = 0;
        int                     count = 0;
        int                     push_xfsbufd = 0;
  
 +      /*
 +       * If last time we ran we encountered pinned items, force the log first
 +       * and wait for it before pushing again.
 +       */
        spin_lock(&ailp->xa_lock);
 +      if (ailp->xa_last_pushed_lsn == 0 && ailp->xa_log_flush &&
 +          !list_empty(&ailp->xa_ail)) {
 +              ailp->xa_log_flush = 0;
 +              spin_unlock(&ailp->xa_lock);
 +              XFS_STATS_INC(xs_push_ail_flush);
 +              xfs_log_force(mp, XFS_LOG_SYNC);
 +              spin_lock(&ailp->xa_lock);
 +      }
 +
        target = ailp->xa_target;
        lip = xfs_trans_ail_cursor_first(ailp, &cur, ailp->xa_last_pushed_lsn);
        if (!lip || XFS_FORCED_SHUTDOWN(mp)) {
  
                case XFS_ITEM_PUSHBUF:
                        XFS_STATS_INC(xs_push_ail_pushbuf);
-                       IOP_PUSHBUF(lip);
-                       ailp->xa_last_pushed_lsn = lsn;
+                       if (!IOP_PUSHBUF(lip)) {
+                               stuck++;
+                               flush_log = 1;
+                       } else {
+                               ailp->xa_last_pushed_lsn = lsn;
+                       }
                        push_xfsbufd = 1;
                        break;
  
                case XFS_ITEM_PINNED:
                        XFS_STATS_INC(xs_push_ail_pinned);
                        stuck++;
 -                      flush_log = 1;
 +                      ailp->xa_log_flush++;
                        break;
  
                case XFS_ITEM_LOCKED:
                        XFS_STATS_INC(xs_push_ail_locked);
-                       ailp->xa_last_pushed_lsn = lsn;
                        stuck++;
                        break;
  
        xfs_trans_ail_cursor_done(ailp, &cur);
        spin_unlock(&ailp->xa_lock);
  
 -      if (flush_log) {
 -              /*
 -               * If something we need to push out was pinned, then
 -               * push out the log so it will become unpinned and
 -               * move forward in the AIL.
 -               */
 -              XFS_STATS_INC(xs_push_ail_flush);
 -              xfs_log_force(mp, 0);
 -      }
 -
        if (push_xfsbufd) {
                /* we've got delayed write buffers to flush */
                wake_up_process(mp->m_ddev_targp->bt_task);
@@@ -502,22 -496,7 +498,8 @@@ out_done
        if (!count) {
                /* We're past our target or empty, so idle */
                ailp->xa_last_pushed_lsn = 0;
 +              ailp->xa_log_flush = 0;
  
-               /*
-                * We clear the XFS_AIL_PUSHING_BIT first before checking
-                * whether the target has changed. If the target has changed,
-                * this pushes the requeue race directly onto the result of the
-                * atomic test/set bit, so we are guaranteed that either the
-                * the pusher that changed the target or ourselves will requeue
-                * the work (but not both).
-                */
-               clear_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags);
-               smp_rmb();
-               if (XFS_LSN_CMP(ailp->xa_target, target) == 0 ||
-                   test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags))
-                       return;
                tout = 50;
        } else if (XFS_LSN_CMP(lsn, target) >= 0) {
                /*
                 * were stuck.
                 *
                 * Backoff a bit more to allow some I/O to complete before
 -               * continuing from where we were.
 +               * restarting from the start of the AIL. This prevents us
 +               * from spinning on the same items, and if they are pinned will
 +               * all the restart to issue a log force to unpin the stuck
 +               * items.
                 */
                tout = 20;
 +              ailp->xa_last_pushed_lsn = 0;
        }
  
-       /* There is more to do, requeue us.  */
-       queue_delayed_work(xfs_syncd_wq, &ailp->xa_work,
-                                       msecs_to_jiffies(tout));
+       return tout;
+ }
+ static int
+ xfsaild(
+       void            *data)
+ {
+       struct xfs_ail  *ailp = data;
+       long            tout = 0;       /* milliseconds */
+       while (!kthread_should_stop()) {
+               if (tout && tout <= 20)
+                       __set_current_state(TASK_KILLABLE);
+               else
+                       __set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(tout ?
+                                msecs_to_jiffies(tout) : MAX_SCHEDULE_TIMEOUT);
+               try_to_freeze();
+               tout = xfsaild_push(ailp);
+       }
+       return 0;
  }
  
  /*
@@@ -581,8 -577,9 +584,9 @@@ xfs_ail_push
         */
        smp_wmb();
        xfs_trans_ail_copy_lsn(ailp, &ailp->xa_target, &threshold_lsn);
-       if (!test_and_set_bit(XFS_AIL_PUSHING_BIT, &ailp->xa_flags))
-               queue_delayed_work(xfs_syncd_wq, &ailp->xa_work, 0);
+       smp_wmb();
+       wake_up_process(ailp->xa_task);
  }
  
  /*
@@@ -820,9 -817,18 +824,18 @@@ xfs_trans_ail_init
        INIT_LIST_HEAD(&ailp->xa_ail);
        INIT_LIST_HEAD(&ailp->xa_cursors);
        spin_lock_init(&ailp->xa_lock);
-       INIT_DELAYED_WORK(&ailp->xa_work, xfs_ail_worker);
+       ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild/%s",
+                       ailp->xa_mount->m_fsname);
+       if (IS_ERR(ailp->xa_task))
+               goto out_free_ailp;
        mp->m_ail = ailp;
        return 0;
+ out_free_ailp:
+       kmem_free(ailp);
+       return ENOMEM;
  }
  
  void
@@@ -831,6 -837,6 +844,6 @@@ xfs_trans_ail_destroy
  {
        struct xfs_ail  *ailp = mp->m_ail;
  
-       cancel_delayed_work_sync(&ailp->xa_work);
+       kthread_stop(ailp->xa_task);
        kmem_free(ailp);
  }
diff --combined fs/xfs/xfs_trans_priv.h
@@@ -64,24 -64,17 +64,18 @@@ struct xfs_ail_cursor 
   */
  struct xfs_ail {
        struct xfs_mount        *xa_mount;
+       struct task_struct      *xa_task;
        struct list_head        xa_ail;
        xfs_lsn_t               xa_target;
        struct list_head        xa_cursors;
        spinlock_t              xa_lock;
-       struct delayed_work     xa_work;
        xfs_lsn_t               xa_last_pushed_lsn;
-       unsigned long           xa_flags;
 +      int                     xa_log_flush;
  };
  
- #define XFS_AIL_PUSHING_BIT   0
  /*
   * From xfs_trans_ail.c
   */
- extern struct workqueue_struct        *xfs_ail_wq;    /* AIL workqueue */
  void  xfs_trans_ail_update_bulk(struct xfs_ail *ailp,
                                struct xfs_ail_cursor *cur,
                                struct xfs_log_item **log_items, int nr_items,