]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
xfs: don't block the log commit handler for discards
authorChristoph Hellwig <hch@lst.de>
Tue, 7 Feb 2017 22:07:58 +0000 (14:07 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Thu, 9 Feb 2017 19:36:40 +0000 (11:36 -0800)
Instead we submit the discard requests and use another workqueue to
release the extents from the extent busy list.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
fs/xfs/xfs_discard.c
fs/xfs/xfs_discard.h
fs/xfs/xfs_log_cil.c
fs/xfs/xfs_log_priv.h
fs/xfs/xfs_mount.c
fs/xfs/xfs_super.c
fs/xfs/xfs_super.h

index 4ff499aa7338f6b7ea098955710938de67791dd4..d796ffac729690ab5273a6007380e76170602cef 100644 (file)
@@ -208,32 +208,3 @@ xfs_ioc_trim(
                return -EFAULT;
        return 0;
 }
-
-int
-xfs_discard_extents(
-       struct xfs_mount        *mp,
-       struct list_head        *list)
-{
-       struct xfs_extent_busy  *busyp;
-       int                     error = 0;
-
-       list_for_each_entry(busyp, list, list) {
-               trace_xfs_discard_extent(mp, busyp->agno, busyp->bno,
-                                        busyp->length);
-
-               error = blkdev_issue_discard(mp->m_ddev_targp->bt_bdev,
-                               XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno),
-                               XFS_FSB_TO_BB(mp, busyp->length),
-                               GFP_NOFS, 0);
-               if (error && error != -EOPNOTSUPP) {
-                       xfs_info(mp,
-        "discard failed for extent [0x%llx,%u], error %d",
-                                (unsigned long long)busyp->bno,
-                                busyp->length,
-                                error);
-                       return error;
-               }
-       }
-
-       return 0;
-}
index 344879aea646cfbaf3ac1c57916f9fce5e6a50c2..0f070f9e44e1440f55dd1cf0b8177632d822d525 100644 (file)
@@ -5,6 +5,5 @@ struct fstrim_range;
 struct list_head;
 
 extern int     xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *);
-extern int     xfs_discard_extents(struct xfs_mount *, struct list_head *);
 
 #endif /* XFS_DISCARD_H */
index a4ab192e1792d1609e4cc309eceeefb5a71ff32f..82f1cbcc4de15ce254322aaa50afb7cdfcb82ae0 100644 (file)
@@ -30,6 +30,9 @@
 #include "xfs_trans_priv.h"
 #include "xfs_log.h"
 #include "xfs_log_priv.h"
+#include "xfs_trace.h"
+
+struct workqueue_struct *xfs_discard_wq;
 
 /*
  * Allocate a new ticket. Failing to get a new ticket makes it really hard to
@@ -491,6 +494,75 @@ xlog_cil_free_logvec(
        }
 }
 
+static void
+xlog_discard_endio_work(
+       struct work_struct      *work)
+{
+       struct xfs_cil_ctx      *ctx =
+               container_of(work, struct xfs_cil_ctx, discard_endio_work);
+       struct xfs_mount        *mp = ctx->cil->xc_log->l_mp;
+
+       xfs_extent_busy_clear(mp, &ctx->busy_extents, false);
+       kmem_free(ctx);
+}
+
+/*
+ * Queue up the actual completion to a thread to avoid IRQ-safe locking for
+ * pagb_lock.  Note that we need a unbounded workqueue, otherwise we might
+ * get the execution delayed up to 30 seconds for weird reasons.
+ */
+static void
+xlog_discard_endio(
+       struct bio              *bio)
+{
+       struct xfs_cil_ctx      *ctx = bio->bi_private;
+
+       INIT_WORK(&ctx->discard_endio_work, xlog_discard_endio_work);
+       queue_work(xfs_discard_wq, &ctx->discard_endio_work);
+}
+
+static void
+xlog_discard_busy_extents(
+       struct xfs_mount        *mp,
+       struct xfs_cil_ctx      *ctx)
+{
+       struct list_head        *list = &ctx->busy_extents;
+       struct xfs_extent_busy  *busyp;
+       struct bio              *bio = NULL;
+       struct blk_plug         plug;
+       int                     error = 0;
+
+       ASSERT(mp->m_flags & XFS_MOUNT_DISCARD);
+
+       blk_start_plug(&plug);
+       list_for_each_entry(busyp, list, list) {
+               trace_xfs_discard_extent(mp, busyp->agno, busyp->bno,
+                                        busyp->length);
+
+               error = __blkdev_issue_discard(mp->m_ddev_targp->bt_bdev,
+                               XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno),
+                               XFS_FSB_TO_BB(mp, busyp->length),
+                               GFP_NOFS, 0, &bio);
+               if (error && error != -EOPNOTSUPP) {
+                       xfs_info(mp,
+        "discard failed for extent [0x%llx,%u], error %d",
+                                (unsigned long long)busyp->bno,
+                                busyp->length,
+                                error);
+                       break;
+               }
+       }
+
+       if (bio) {
+               bio->bi_private = ctx;
+               bio->bi_end_io = xlog_discard_endio;
+               submit_bio(bio);
+       } else {
+               xlog_discard_endio_work(&ctx->discard_endio_work);
+       }
+       blk_finish_plug(&plug);
+}
+
 /*
  * Mark all items committed and clear busy extents. We free the log vector
  * chains in a separate pass so that we unpin the log items as quickly as
@@ -525,14 +597,10 @@ xlog_cil_committed(
 
        xlog_cil_free_logvec(ctx->lv_chain);
 
-       if (!list_empty(&ctx->busy_extents)) {
-               ASSERT(mp->m_flags & XFS_MOUNT_DISCARD);
-
-               xfs_discard_extents(mp, &ctx->busy_extents);
-               xfs_extent_busy_clear(mp, &ctx->busy_extents, false);
-       }
-
-       kmem_free(ctx);
+       if (!list_empty(&ctx->busy_extents))
+               xlog_discard_busy_extents(mp, ctx);
+       else
+               kmem_free(ctx);
 }
 
 /*
index 2b6eec52178e947e0e114753fcf1f1bfaaf51775..c2604a5366f274cc5d1268a633a5885a05fd0446 100644 (file)
@@ -257,6 +257,7 @@ struct xfs_cil_ctx {
        struct xfs_log_vec      *lv_chain;      /* logvecs being pushed */
        struct xfs_log_callback log_cb;         /* completion callback hook. */
        struct list_head        committing;     /* ctx committing list */
+       struct work_struct      discard_endio_work;
 };
 
 /*
index 566eb79666ad9b6b57bc789ca2d8b3778a732e68..450bde68bb7528d70a47e0b38275ca75c7e757a1 100644 (file)
@@ -1085,6 +1085,7 @@ xfs_unmountfs(
         * any discard operation.
         */
        xfs_extent_busy_wait_all(mp);
+       flush_workqueue(xfs_discard_wq);
 
        /*
         * We now need to tell the world we are unmounting. This will allow
index eecbaac08ebaab50f7a5a139e0ae646a7c086d4d..890862f2447c193f374b4de64c58940521b203fb 100644 (file)
@@ -1956,12 +1956,20 @@ xfs_init_workqueues(void)
        if (!xfs_alloc_wq)
                return -ENOMEM;
 
+       xfs_discard_wq = alloc_workqueue("xfsdiscard", WQ_UNBOUND, 0);
+       if (!xfs_discard_wq)
+               goto out_free_alloc_wq;
+
        return 0;
+out_free_alloc_wq:
+       destroy_workqueue(xfs_alloc_wq);
+       return -ENOMEM;
 }
 
 STATIC void
 xfs_destroy_workqueues(void)
 {
+       destroy_workqueue(xfs_discard_wq);
        destroy_workqueue(xfs_alloc_wq);
 }
 
index b6418abd85adb92dffa766e1b56a75cc2114e1c4..5f2f32408011d2df4db8ddc6d41297640bab7101 100644 (file)
@@ -73,6 +73,8 @@ extern const struct quotactl_ops xfs_quotactl_operations;
 
 extern void xfs_reinit_percpu_counters(struct xfs_mount *mp);
 
+extern struct workqueue_struct *xfs_discard_wq;
+
 #define XFS_M(sb)              ((struct xfs_mount *)((sb)->s_fs_info))
 
 #endif /* __XFS_SUPER_H__ */