diff options
Diffstat (limited to 'fs/xfs')
-rw-r--r-- | fs/xfs/linux-2.6/xfs_discard.c | 29 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_discard.h | 2 | ||||
-rw-r--r-- | fs/xfs/linux-2.6/xfs_super.c | 18 | ||||
-rw-r--r-- | fs/xfs/xfs_ag.h | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_alloc.c | 28 | ||||
-rw-r--r-- | fs/xfs/xfs_alloc.h | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_log_cil.c | 13 | ||||
-rw-r--r-- | fs/xfs/xfs_mount.h | 1 | ||||
-rw-r--r-- | fs/xfs/xfs_trans.c | 2 |
9 files changed, 90 insertions, 8 deletions
diff --git a/fs/xfs/linux-2.6/xfs_discard.c b/fs/xfs/linux-2.6/xfs_discard.c index d61611c88012..244e797dae32 100644 --- a/fs/xfs/linux-2.6/xfs_discard.c +++ b/fs/xfs/linux-2.6/xfs_discard.c | |||
@@ -191,3 +191,32 @@ xfs_ioc_trim( | |||
191 | return -XFS_ERROR(EFAULT); | 191 | return -XFS_ERROR(EFAULT); |
192 | return 0; | 192 | return 0; |
193 | } | 193 | } |
194 | |||
195 | int | ||
196 | xfs_discard_extents( | ||
197 | struct xfs_mount *mp, | ||
198 | struct list_head *list) | ||
199 | { | ||
200 | struct xfs_busy_extent *busyp; | ||
201 | int error = 0; | ||
202 | |||
203 | list_for_each_entry(busyp, list, list) { | ||
204 | trace_xfs_discard_extent(mp, busyp->agno, busyp->bno, | ||
205 | busyp->length); | ||
206 | |||
207 | error = -blkdev_issue_discard(mp->m_ddev_targp->bt_bdev, | ||
208 | XFS_AGB_TO_DADDR(mp, busyp->agno, busyp->bno), | ||
209 | XFS_FSB_TO_BB(mp, busyp->length), | ||
210 | GFP_NOFS, 0); | ||
211 | if (error && error != EOPNOTSUPP) { | ||
212 | xfs_info(mp, | ||
213 | "discard failed for extent [0x%llu,%u], error %d", | ||
214 | (unsigned long long)busyp->bno, | ||
215 | busyp->length, | ||
216 | error); | ||
217 | return error; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
diff --git a/fs/xfs/linux-2.6/xfs_discard.h b/fs/xfs/linux-2.6/xfs_discard.h index e82b6dd3e127..344879aea646 100644 --- a/fs/xfs/linux-2.6/xfs_discard.h +++ b/fs/xfs/linux-2.6/xfs_discard.h | |||
@@ -2,7 +2,9 @@ | |||
2 | #define XFS_DISCARD_H 1 | 2 | #define XFS_DISCARD_H 1 |
3 | 3 | ||
4 | struct fstrim_range; | 4 | struct fstrim_range; |
5 | struct list_head; | ||
5 | 6 | ||
6 | extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *); | 7 | extern int xfs_ioc_trim(struct xfs_mount *, struct fstrim_range __user *); |
8 | extern int xfs_discard_extents(struct xfs_mount *, struct list_head *); | ||
7 | 9 | ||
8 | #endif /* XFS_DISCARD_H */ | 10 | #endif /* XFS_DISCARD_H */ |
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index b0aa59e51fd0..98b9c91fcdf1 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c | |||
@@ -110,8 +110,10 @@ mempool_t *xfs_ioend_pool; | |||
110 | #define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */ | 110 | #define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */ |
111 | #define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */ | 111 | #define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */ |
112 | #define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */ | 112 | #define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */ |
113 | #define MNTOPT_DELAYLOG "delaylog" /* Delayed loging enabled */ | 113 | #define MNTOPT_DELAYLOG "delaylog" /* Delayed logging enabled */ |
114 | #define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed loging disabled */ | 114 | #define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed logging disabled */ |
115 | #define MNTOPT_DISCARD "discard" /* Discard unused blocks */ | ||
116 | #define MNTOPT_NODISCARD "nodiscard" /* Do not discard unused blocks */ | ||
115 | 117 | ||
116 | /* | 118 | /* |
117 | * Table driven mount option parser. | 119 | * Table driven mount option parser. |
@@ -355,6 +357,10 @@ xfs_parseargs( | |||
355 | mp->m_flags |= XFS_MOUNT_DELAYLOG; | 357 | mp->m_flags |= XFS_MOUNT_DELAYLOG; |
356 | } else if (!strcmp(this_char, MNTOPT_NODELAYLOG)) { | 358 | } else if (!strcmp(this_char, MNTOPT_NODELAYLOG)) { |
357 | mp->m_flags &= ~XFS_MOUNT_DELAYLOG; | 359 | mp->m_flags &= ~XFS_MOUNT_DELAYLOG; |
360 | } else if (!strcmp(this_char, MNTOPT_DISCARD)) { | ||
361 | mp->m_flags |= XFS_MOUNT_DISCARD; | ||
362 | } else if (!strcmp(this_char, MNTOPT_NODISCARD)) { | ||
363 | mp->m_flags &= ~XFS_MOUNT_DISCARD; | ||
358 | } else if (!strcmp(this_char, "ihashsize")) { | 364 | } else if (!strcmp(this_char, "ihashsize")) { |
359 | xfs_warn(mp, | 365 | xfs_warn(mp, |
360 | "ihashsize no longer used, option is deprecated."); | 366 | "ihashsize no longer used, option is deprecated."); |
@@ -388,6 +394,13 @@ xfs_parseargs( | |||
388 | return EINVAL; | 394 | return EINVAL; |
389 | } | 395 | } |
390 | 396 | ||
397 | if ((mp->m_flags & XFS_MOUNT_DISCARD) && | ||
398 | !(mp->m_flags & XFS_MOUNT_DELAYLOG)) { | ||
399 | xfs_warn(mp, | ||
400 | "the discard option is incompatible with the nodelaylog option"); | ||
401 | return EINVAL; | ||
402 | } | ||
403 | |||
391 | #ifndef CONFIG_XFS_QUOTA | 404 | #ifndef CONFIG_XFS_QUOTA |
392 | if (XFS_IS_QUOTA_RUNNING(mp)) { | 405 | if (XFS_IS_QUOTA_RUNNING(mp)) { |
393 | xfs_warn(mp, "quota support not available in this kernel."); | 406 | xfs_warn(mp, "quota support not available in this kernel."); |
@@ -488,6 +501,7 @@ xfs_showargs( | |||
488 | { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, | 501 | { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, |
489 | { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, | 502 | { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, |
490 | { XFS_MOUNT_DELAYLOG, "," MNTOPT_DELAYLOG }, | 503 | { XFS_MOUNT_DELAYLOG, "," MNTOPT_DELAYLOG }, |
504 | { XFS_MOUNT_DISCARD, "," MNTOPT_DISCARD }, | ||
491 | { 0, NULL } | 505 | { 0, NULL } |
492 | }; | 506 | }; |
493 | static struct proc_xfs_info xfs_info_unset[] = { | 507 | static struct proc_xfs_info xfs_info_unset[] = { |
diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index da0a561ffba2..8d52ba4c87e5 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h | |||
@@ -187,6 +187,8 @@ struct xfs_busy_extent { | |||
187 | xfs_agnumber_t agno; | 187 | xfs_agnumber_t agno; |
188 | xfs_agblock_t bno; | 188 | xfs_agblock_t bno; |
189 | xfs_extlen_t length; | 189 | xfs_extlen_t length; |
190 | unsigned int flags; | ||
191 | #define XFS_ALLOC_BUSY_DISCARDED 0x01 /* undergoing a discard op. */ | ||
190 | }; | 192 | }; |
191 | 193 | ||
192 | /* | 194 | /* |
diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index acdced86413c..721db22c6ec9 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c | |||
@@ -2609,6 +2609,18 @@ xfs_alloc_busy_update_extent( | |||
2609 | xfs_agblock_t bend = bbno + busyp->length; | 2609 | xfs_agblock_t bend = bbno + busyp->length; |
2610 | 2610 | ||
2611 | /* | 2611 | /* |
2612 | * This extent is currently being discarded. Give the thread | ||
2613 | * performing the discard a chance to mark the extent unbusy | ||
2614 | * and retry. | ||
2615 | */ | ||
2616 | if (busyp->flags & XFS_ALLOC_BUSY_DISCARDED) { | ||
2617 | spin_unlock(&pag->pagb_lock); | ||
2618 | delay(1); | ||
2619 | spin_lock(&pag->pagb_lock); | ||
2620 | return false; | ||
2621 | } | ||
2622 | |||
2623 | /* | ||
2612 | * If there is a busy extent overlapping a user allocation, we have | 2624 | * If there is a busy extent overlapping a user allocation, we have |
2613 | * no choice but to force the log and retry the search. | 2625 | * no choice but to force the log and retry the search. |
2614 | * | 2626 | * |
@@ -2813,7 +2825,8 @@ restart: | |||
2813 | * If this is a metadata allocation, try to reuse the busy | 2825 | * If this is a metadata allocation, try to reuse the busy |
2814 | * extent instead of trimming the allocation. | 2826 | * extent instead of trimming the allocation. |
2815 | */ | 2827 | */ |
2816 | if (!args->userdata) { | 2828 | if (!args->userdata && |
2829 | !(busyp->flags & XFS_ALLOC_BUSY_DISCARDED)) { | ||
2817 | if (!xfs_alloc_busy_update_extent(args->mp, args->pag, | 2830 | if (!xfs_alloc_busy_update_extent(args->mp, args->pag, |
2818 | busyp, fbno, flen, | 2831 | busyp, fbno, flen, |
2819 | false)) | 2832 | false)) |
@@ -2979,10 +2992,16 @@ xfs_alloc_busy_clear_one( | |||
2979 | kmem_free(busyp); | 2992 | kmem_free(busyp); |
2980 | } | 2993 | } |
2981 | 2994 | ||
2995 | /* | ||
2996 | * Remove all extents on the passed in list from the busy extents tree. | ||
2997 | * If do_discard is set skip extents that need to be discarded, and mark | ||
2998 | * these as undergoing a discard operation instead. | ||
2999 | */ | ||
2982 | void | 3000 | void |
2983 | xfs_alloc_busy_clear( | 3001 | xfs_alloc_busy_clear( |
2984 | struct xfs_mount *mp, | 3002 | struct xfs_mount *mp, |
2985 | struct list_head *list) | 3003 | struct list_head *list, |
3004 | bool do_discard) | ||
2986 | { | 3005 | { |
2987 | struct xfs_busy_extent *busyp, *n; | 3006 | struct xfs_busy_extent *busyp, *n; |
2988 | struct xfs_perag *pag = NULL; | 3007 | struct xfs_perag *pag = NULL; |
@@ -2999,7 +3018,10 @@ xfs_alloc_busy_clear( | |||
2999 | agno = busyp->agno; | 3018 | agno = busyp->agno; |
3000 | } | 3019 | } |
3001 | 3020 | ||
3002 | xfs_alloc_busy_clear_one(mp, pag, busyp); | 3021 | if (do_discard && busyp->length) |
3022 | busyp->flags = XFS_ALLOC_BUSY_DISCARDED; | ||
3023 | else | ||
3024 | xfs_alloc_busy_clear_one(mp, pag, busyp); | ||
3003 | } | 3025 | } |
3004 | 3026 | ||
3005 | if (pag) { | 3027 | if (pag) { |
diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h index 240ad288f2f9..06aa8217452b 100644 --- a/fs/xfs/xfs_alloc.h +++ b/fs/xfs/xfs_alloc.h | |||
@@ -140,7 +140,8 @@ xfs_alloc_busy_insert(struct xfs_trans *tp, xfs_agnumber_t agno, | |||
140 | xfs_agblock_t bno, xfs_extlen_t len); | 140 | xfs_agblock_t bno, xfs_extlen_t len); |
141 | 141 | ||
142 | void | 142 | void |
143 | xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list); | 143 | xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list, |
144 | bool do_discard); | ||
144 | 145 | ||
145 | int | 146 | int |
146 | xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno, | 147 | xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno, |
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 7d56e88a3f0e..c7755d5a5fbe 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "xfs_mount.h" | 29 | #include "xfs_mount.h" |
30 | #include "xfs_error.h" | 30 | #include "xfs_error.h" |
31 | #include "xfs_alloc.h" | 31 | #include "xfs_alloc.h" |
32 | #include "xfs_discard.h" | ||
32 | 33 | ||
33 | /* | 34 | /* |
34 | * Perform initial CIL structure initialisation. If the CIL is not | 35 | * Perform initial CIL structure initialisation. If the CIL is not |
@@ -361,18 +362,28 @@ xlog_cil_committed( | |||
361 | int abort) | 362 | int abort) |
362 | { | 363 | { |
363 | struct xfs_cil_ctx *ctx = args; | 364 | struct xfs_cil_ctx *ctx = args; |
365 | struct xfs_mount *mp = ctx->cil->xc_log->l_mp; | ||
364 | 366 | ||
365 | xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain, | 367 | xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain, |
366 | ctx->start_lsn, abort); | 368 | ctx->start_lsn, abort); |
367 | 369 | ||
368 | xfs_alloc_busy_sort(&ctx->busy_extents); | 370 | xfs_alloc_busy_sort(&ctx->busy_extents); |
369 | xfs_alloc_busy_clear(ctx->cil->xc_log->l_mp, &ctx->busy_extents); | 371 | xfs_alloc_busy_clear(mp, &ctx->busy_extents, |
372 | (mp->m_flags & XFS_MOUNT_DISCARD) && !abort); | ||
370 | 373 | ||
371 | spin_lock(&ctx->cil->xc_cil_lock); | 374 | spin_lock(&ctx->cil->xc_cil_lock); |
372 | list_del(&ctx->committing); | 375 | list_del(&ctx->committing); |
373 | spin_unlock(&ctx->cil->xc_cil_lock); | 376 | spin_unlock(&ctx->cil->xc_cil_lock); |
374 | 377 | ||
375 | xlog_cil_free_logvec(ctx->lv_chain); | 378 | xlog_cil_free_logvec(ctx->lv_chain); |
379 | |||
380 | if (!list_empty(&ctx->busy_extents)) { | ||
381 | ASSERT(mp->m_flags & XFS_MOUNT_DISCARD); | ||
382 | |||
383 | xfs_discard_extents(mp, &ctx->busy_extents); | ||
384 | xfs_alloc_busy_clear(mp, &ctx->busy_extents, false); | ||
385 | } | ||
386 | |||
376 | kmem_free(ctx); | 387 | kmem_free(ctx); |
377 | } | 388 | } |
378 | 389 | ||
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 19af0ab0d0c6..3d68bb267c5f 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h | |||
@@ -224,6 +224,7 @@ typedef struct xfs_mount { | |||
224 | #define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem | 224 | #define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem |
225 | operations, typically for | 225 | operations, typically for |
226 | disk errors in metadata */ | 226 | disk errors in metadata */ |
227 | #define XFS_MOUNT_DISCARD (1ULL << 5) /* discard unused blocks */ | ||
227 | #define XFS_MOUNT_RETERR (1ULL << 6) /* return alignment errors to | 228 | #define XFS_MOUNT_RETERR (1ULL << 6) /* return alignment errors to |
228 | user */ | 229 | user */ |
229 | #define XFS_MOUNT_NOALIGN (1ULL << 7) /* turn off stripe alignment | 230 | #define XFS_MOUNT_NOALIGN (1ULL << 7) /* turn off stripe alignment |
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index d1f24858ccc4..7c7bc2b786bd 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c | |||
@@ -609,7 +609,7 @@ xfs_trans_free( | |||
609 | struct xfs_trans *tp) | 609 | struct xfs_trans *tp) |
610 | { | 610 | { |
611 | xfs_alloc_busy_sort(&tp->t_busy); | 611 | xfs_alloc_busy_sort(&tp->t_busy); |
612 | xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy); | 612 | xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy, false); |
613 | 613 | ||
614 | atomic_dec(&tp->t_mountp->m_active_trans); | 614 | atomic_dec(&tp->t_mountp->m_active_trans); |
615 | xfs_trans_free_dqinfo(tp); | 615 | xfs_trans_free_dqinfo(tp); |