Commit 4560e78f authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Darrick J. Wong

xfs: don't block the log commit handler for discards

Instead we submit the discard requests and use another workqueue to
release the extents from the extent busy list.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
parent 46694129
...@@ -208,32 +208,3 @@ xfs_ioc_trim( ...@@ -208,32 +208,3 @@ xfs_ioc_trim(
return -EFAULT; return -EFAULT;
return 0; 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;
}
...@@ -5,6 +5,5 @@ struct fstrim_range; ...@@ -5,6 +5,5 @@ struct fstrim_range;
struct list_head; struct list_head;
extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *); 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 */ #endif /* XFS_DISCARD_H */
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
#include "xfs_trans_priv.h" #include "xfs_trans_priv.h"
#include "xfs_log.h" #include "xfs_log.h"
#include "xfs_log_priv.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 * Allocate a new ticket. Failing to get a new ticket makes it really hard to
...@@ -491,6 +494,75 @@ xlog_cil_free_logvec( ...@@ -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 * 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 * chains in a separate pass so that we unpin the log items as quickly as
...@@ -525,14 +597,10 @@ xlog_cil_committed( ...@@ -525,14 +597,10 @@ xlog_cil_committed(
xlog_cil_free_logvec(ctx->lv_chain); xlog_cil_free_logvec(ctx->lv_chain);
if (!list_empty(&ctx->busy_extents)) { if (!list_empty(&ctx->busy_extents))
ASSERT(mp->m_flags & XFS_MOUNT_DISCARD); xlog_discard_busy_extents(mp, ctx);
else
xfs_discard_extents(mp, &ctx->busy_extents); kmem_free(ctx);
xfs_extent_busy_clear(mp, &ctx->busy_extents, false);
}
kmem_free(ctx);
} }
/* /*
......
...@@ -257,6 +257,7 @@ struct xfs_cil_ctx { ...@@ -257,6 +257,7 @@ struct xfs_cil_ctx {
struct xfs_log_vec *lv_chain; /* logvecs being pushed */ struct xfs_log_vec *lv_chain; /* logvecs being pushed */
struct xfs_log_callback log_cb; /* completion callback hook. */ struct xfs_log_callback log_cb; /* completion callback hook. */
struct list_head committing; /* ctx committing list */ struct list_head committing; /* ctx committing list */
struct work_struct discard_endio_work;
}; };
/* /*
......
...@@ -1085,6 +1085,7 @@ xfs_unmountfs( ...@@ -1085,6 +1085,7 @@ xfs_unmountfs(
* any discard operation. * any discard operation.
*/ */
xfs_extent_busy_wait_all(mp); xfs_extent_busy_wait_all(mp);
flush_workqueue(xfs_discard_wq);
/* /*
* We now need to tell the world we are unmounting. This will allow * We now need to tell the world we are unmounting. This will allow
......
...@@ -1956,12 +1956,20 @@ xfs_init_workqueues(void) ...@@ -1956,12 +1956,20 @@ xfs_init_workqueues(void)
if (!xfs_alloc_wq) if (!xfs_alloc_wq)
return -ENOMEM; return -ENOMEM;
xfs_discard_wq = alloc_workqueue("xfsdiscard", WQ_UNBOUND, 0);
if (!xfs_discard_wq)
goto out_free_alloc_wq;
return 0; return 0;
out_free_alloc_wq:
destroy_workqueue(xfs_alloc_wq);
return -ENOMEM;
} }
STATIC void STATIC void
xfs_destroy_workqueues(void) xfs_destroy_workqueues(void)
{ {
destroy_workqueue(xfs_discard_wq);
destroy_workqueue(xfs_alloc_wq); destroy_workqueue(xfs_alloc_wq);
} }
......
...@@ -73,6 +73,8 @@ extern const struct quotactl_ops xfs_quotactl_operations; ...@@ -73,6 +73,8 @@ extern const struct quotactl_ops xfs_quotactl_operations;
extern void xfs_reinit_percpu_counters(struct xfs_mount *mp); 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)) #define XFS_M(sb) ((struct xfs_mount *)((sb)->s_fs_info))
#endif /* __XFS_SUPER_H__ */ #endif /* __XFS_SUPER_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment