forked from Minki/linux
Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
* 'for-linus' of git://oss.sgi.com/xfs/xfs: xfs: correctly decrement the extent buffer index in xfs_bmap_del_extent xfs: check for valid indices in xfs_iext_get_ext and xfs_iext_idx_to_irec xfs: fix up asserts in xfs_iflush_fork xfs: do not do pointer arithmetic on extent records xfs: do not use unchecked extent indices in xfs_bunmapi xfs: do not use unchecked extent indices in xfs_bmapi xfs: do not use unchecked extent indices in xfs_bmap_add_extent_* xfs: remove if_lastex xfs: remove the unused XFS_BMAPI_RSVBLOCKS flag xfs: do not discard alloc btree blocks xfs: add online discard support
This commit is contained in:
commit
8a0599dd24
@ -39,6 +39,12 @@ When mounting an XFS filesystem, the following options are accepted.
|
||||
drive level write caching to be enabled, for devices that
|
||||
support write barriers.
|
||||
|
||||
discard
|
||||
Issue command to let the block device reclaim space freed by the
|
||||
filesystem. This is useful for SSD devices, thinly provisioned
|
||||
LUNs and virtual machine images, but may have a performance
|
||||
impact. This option is incompatible with the nodelaylog option.
|
||||
|
||||
dmapi
|
||||
Enable the DMAPI (Data Management API) event callouts.
|
||||
Use with the "mtpt" option.
|
||||
|
@ -191,3 +191,32 @@ xfs_ioc_trim(
|
||||
return -XFS_ERROR(EFAULT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
xfs_discard_extents(
|
||||
struct xfs_mount *mp,
|
||||
struct list_head *list)
|
||||
{
|
||||
struct xfs_busy_extent *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%llu,%u], error %d",
|
||||
(unsigned long long)busyp->bno,
|
||||
busyp->length,
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
#define XFS_DISCARD_H 1
|
||||
|
||||
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 */
|
||||
|
@ -110,8 +110,10 @@ mempool_t *xfs_ioend_pool;
|
||||
#define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */
|
||||
#define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */
|
||||
#define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */
|
||||
#define MNTOPT_DELAYLOG "delaylog" /* Delayed loging enabled */
|
||||
#define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed loging disabled */
|
||||
#define MNTOPT_DELAYLOG "delaylog" /* Delayed logging enabled */
|
||||
#define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed logging disabled */
|
||||
#define MNTOPT_DISCARD "discard" /* Discard unused blocks */
|
||||
#define MNTOPT_NODISCARD "nodiscard" /* Do not discard unused blocks */
|
||||
|
||||
/*
|
||||
* Table driven mount option parser.
|
||||
@ -355,6 +357,10 @@ xfs_parseargs(
|
||||
mp->m_flags |= XFS_MOUNT_DELAYLOG;
|
||||
} else if (!strcmp(this_char, MNTOPT_NODELAYLOG)) {
|
||||
mp->m_flags &= ~XFS_MOUNT_DELAYLOG;
|
||||
} else if (!strcmp(this_char, MNTOPT_DISCARD)) {
|
||||
mp->m_flags |= XFS_MOUNT_DISCARD;
|
||||
} else if (!strcmp(this_char, MNTOPT_NODISCARD)) {
|
||||
mp->m_flags &= ~XFS_MOUNT_DISCARD;
|
||||
} else if (!strcmp(this_char, "ihashsize")) {
|
||||
xfs_warn(mp,
|
||||
"ihashsize no longer used, option is deprecated.");
|
||||
@ -388,6 +394,13 @@ xfs_parseargs(
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((mp->m_flags & XFS_MOUNT_DISCARD) &&
|
||||
!(mp->m_flags & XFS_MOUNT_DELAYLOG)) {
|
||||
xfs_warn(mp,
|
||||
"the discard option is incompatible with the nodelaylog option");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_XFS_QUOTA
|
||||
if (XFS_IS_QUOTA_RUNNING(mp)) {
|
||||
xfs_warn(mp, "quota support not available in this kernel.");
|
||||
@ -488,6 +501,7 @@ xfs_showargs(
|
||||
{ XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM },
|
||||
{ XFS_MOUNT_GRPID, "," MNTOPT_GRPID },
|
||||
{ XFS_MOUNT_DELAYLOG, "," MNTOPT_DELAYLOG },
|
||||
{ XFS_MOUNT_DISCARD, "," MNTOPT_DISCARD },
|
||||
{ 0, NULL }
|
||||
};
|
||||
static struct proc_xfs_info xfs_info_unset[] = {
|
||||
|
@ -187,6 +187,9 @@ struct xfs_busy_extent {
|
||||
xfs_agnumber_t agno;
|
||||
xfs_agblock_t bno;
|
||||
xfs_extlen_t length;
|
||||
unsigned int flags;
|
||||
#define XFS_ALLOC_BUSY_DISCARDED 0x01 /* undergoing a discard op. */
|
||||
#define XFS_ALLOC_BUSY_SKIP_DISCARD 0x02 /* do not discard */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -2469,7 +2469,7 @@ xfs_free_extent(
|
||||
|
||||
error = xfs_free_ag_extent(tp, args.agbp, args.agno, args.agbno, len, 0);
|
||||
if (!error)
|
||||
xfs_alloc_busy_insert(tp, args.agno, args.agbno, len);
|
||||
xfs_alloc_busy_insert(tp, args.agno, args.agbno, len, 0);
|
||||
error0:
|
||||
xfs_perag_put(args.pag);
|
||||
return error;
|
||||
@ -2480,7 +2480,8 @@ xfs_alloc_busy_insert(
|
||||
struct xfs_trans *tp,
|
||||
xfs_agnumber_t agno,
|
||||
xfs_agblock_t bno,
|
||||
xfs_extlen_t len)
|
||||
xfs_extlen_t len,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct xfs_busy_extent *new;
|
||||
struct xfs_busy_extent *busyp;
|
||||
@ -2504,6 +2505,7 @@ xfs_alloc_busy_insert(
|
||||
new->bno = bno;
|
||||
new->length = len;
|
||||
INIT_LIST_HEAD(&new->list);
|
||||
new->flags = flags;
|
||||
|
||||
/* trace before insert to be able to see failed inserts */
|
||||
trace_xfs_alloc_busy(tp->t_mountp, agno, bno, len);
|
||||
@ -2608,6 +2610,18 @@ xfs_alloc_busy_update_extent(
|
||||
xfs_agblock_t bbno = busyp->bno;
|
||||
xfs_agblock_t bend = bbno + busyp->length;
|
||||
|
||||
/*
|
||||
* This extent is currently being discarded. Give the thread
|
||||
* performing the discard a chance to mark the extent unbusy
|
||||
* and retry.
|
||||
*/
|
||||
if (busyp->flags & XFS_ALLOC_BUSY_DISCARDED) {
|
||||
spin_unlock(&pag->pagb_lock);
|
||||
delay(1);
|
||||
spin_lock(&pag->pagb_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is a busy extent overlapping a user allocation, we have
|
||||
* no choice but to force the log and retry the search.
|
||||
@ -2813,7 +2827,8 @@ restart:
|
||||
* If this is a metadata allocation, try to reuse the busy
|
||||
* extent instead of trimming the allocation.
|
||||
*/
|
||||
if (!args->userdata) {
|
||||
if (!args->userdata &&
|
||||
!(busyp->flags & XFS_ALLOC_BUSY_DISCARDED)) {
|
||||
if (!xfs_alloc_busy_update_extent(args->mp, args->pag,
|
||||
busyp, fbno, flen,
|
||||
false))
|
||||
@ -2979,10 +2994,16 @@ xfs_alloc_busy_clear_one(
|
||||
kmem_free(busyp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all extents on the passed in list from the busy extents tree.
|
||||
* If do_discard is set skip extents that need to be discarded, and mark
|
||||
* these as undergoing a discard operation instead.
|
||||
*/
|
||||
void
|
||||
xfs_alloc_busy_clear(
|
||||
struct xfs_mount *mp,
|
||||
struct list_head *list)
|
||||
struct list_head *list,
|
||||
bool do_discard)
|
||||
{
|
||||
struct xfs_busy_extent *busyp, *n;
|
||||
struct xfs_perag *pag = NULL;
|
||||
@ -2999,7 +3020,11 @@ xfs_alloc_busy_clear(
|
||||
agno = busyp->agno;
|
||||
}
|
||||
|
||||
xfs_alloc_busy_clear_one(mp, pag, busyp);
|
||||
if (do_discard && busyp->length &&
|
||||
!(busyp->flags & XFS_ALLOC_BUSY_SKIP_DISCARD))
|
||||
busyp->flags = XFS_ALLOC_BUSY_DISCARDED;
|
||||
else
|
||||
xfs_alloc_busy_clear_one(mp, pag, busyp);
|
||||
}
|
||||
|
||||
if (pag) {
|
||||
|
@ -137,10 +137,11 @@ xfs_alloc_longest_free_extent(struct xfs_mount *mp,
|
||||
#ifdef __KERNEL__
|
||||
void
|
||||
xfs_alloc_busy_insert(struct xfs_trans *tp, xfs_agnumber_t agno,
|
||||
xfs_agblock_t bno, xfs_extlen_t len);
|
||||
xfs_agblock_t bno, xfs_extlen_t len, unsigned int flags);
|
||||
|
||||
void
|
||||
xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list);
|
||||
xfs_alloc_busy_clear(struct xfs_mount *mp, struct list_head *list,
|
||||
bool do_discard);
|
||||
|
||||
int
|
||||
xfs_alloc_busy_search(struct xfs_mount *mp, xfs_agnumber_t agno,
|
||||
|
@ -120,7 +120,8 @@ xfs_allocbt_free_block(
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_alloc_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1);
|
||||
xfs_alloc_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1,
|
||||
XFS_ALLOC_BUSY_SKIP_DISCARD);
|
||||
xfs_trans_agbtree_delta(cur->bc_tp, -1);
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,6 @@ typedef struct xfs_bmap_free
|
||||
#define XFS_BMAPI_ENTIRE 0x004 /* return entire extent, not trimmed */
|
||||
#define XFS_BMAPI_METADATA 0x008 /* mapping metadata not user data */
|
||||
#define XFS_BMAPI_ATTRFORK 0x010 /* use attribute fork not data */
|
||||
#define XFS_BMAPI_RSVBLOCKS 0x020 /* OK to alloc. reserved data blocks */
|
||||
#define XFS_BMAPI_PREALLOC 0x040 /* preallocation op: unwritten space */
|
||||
#define XFS_BMAPI_IGSTATE 0x080 /* Ignore state - */
|
||||
/* combine contig. space */
|
||||
@ -87,7 +86,6 @@ typedef struct xfs_bmap_free
|
||||
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
|
||||
{ XFS_BMAPI_METADATA, "METADATA" }, \
|
||||
{ XFS_BMAPI_ATTRFORK, "ATTRFORK" }, \
|
||||
{ XFS_BMAPI_RSVBLOCKS, "RSVBLOCKS" }, \
|
||||
{ XFS_BMAPI_PREALLOC, "PREALLOC" }, \
|
||||
{ XFS_BMAPI_IGSTATE, "IGSTATE" }, \
|
||||
{ XFS_BMAPI_CONTIG, "CONTIG" }, \
|
||||
|
@ -920,7 +920,6 @@ xfs_iread_extents(
|
||||
/*
|
||||
* We know that the size is valid (it's checked in iformat_btree)
|
||||
*/
|
||||
ifp->if_lastex = NULLEXTNUM;
|
||||
ifp->if_bytes = ifp->if_real_bytes = 0;
|
||||
ifp->if_flags |= XFS_IFEXTENTS;
|
||||
xfs_iext_add(ifp, 0, nextents);
|
||||
@ -2558,12 +2557,9 @@ xfs_iflush_fork(
|
||||
case XFS_DINODE_FMT_EXTENTS:
|
||||
ASSERT((ifp->if_flags & XFS_IFEXTENTS) ||
|
||||
!(iip->ili_format.ilf_fields & extflag[whichfork]));
|
||||
ASSERT((xfs_iext_get_ext(ifp, 0) != NULL) ||
|
||||
(ifp->if_bytes == 0));
|
||||
ASSERT((xfs_iext_get_ext(ifp, 0) == NULL) ||
|
||||
(ifp->if_bytes > 0));
|
||||
if ((iip->ili_format.ilf_fields & extflag[whichfork]) &&
|
||||
(ifp->if_bytes > 0)) {
|
||||
ASSERT(xfs_iext_get_ext(ifp, 0));
|
||||
ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) > 0);
|
||||
(void)xfs_iextents_copy(ip, (xfs_bmbt_rec_t *)cp,
|
||||
whichfork);
|
||||
@ -3112,6 +3108,8 @@ xfs_iext_get_ext(
|
||||
xfs_extnum_t idx) /* index of target extent */
|
||||
{
|
||||
ASSERT(idx >= 0);
|
||||
ASSERT(idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
|
||||
|
||||
if ((ifp->if_flags & XFS_IFEXTIREC) && (idx == 0)) {
|
||||
return ifp->if_u1.if_ext_irec->er_extbuf;
|
||||
} else if (ifp->if_flags & XFS_IFEXTIREC) {
|
||||
@ -3191,7 +3189,6 @@ xfs_iext_add(
|
||||
}
|
||||
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
|
||||
ifp->if_real_bytes = 0;
|
||||
ifp->if_lastex = nextents + ext_diff;
|
||||
}
|
||||
/*
|
||||
* Otherwise use a linear (direct) extent list.
|
||||
@ -3886,8 +3883,10 @@ xfs_iext_idx_to_irec(
|
||||
xfs_extnum_t page_idx = *idxp; /* extent index in target list */
|
||||
|
||||
ASSERT(ifp->if_flags & XFS_IFEXTIREC);
|
||||
ASSERT(page_idx >= 0 && page_idx <=
|
||||
ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t));
|
||||
ASSERT(page_idx >= 0);
|
||||
ASSERT(page_idx <= ifp->if_bytes / sizeof(xfs_bmbt_rec_t));
|
||||
ASSERT(page_idx < ifp->if_bytes / sizeof(xfs_bmbt_rec_t) || realloc);
|
||||
|
||||
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
|
||||
erp_idx = 0;
|
||||
low = 0;
|
||||
|
@ -67,7 +67,6 @@ typedef struct xfs_ifork {
|
||||
short if_broot_bytes; /* bytes allocated for root */
|
||||
unsigned char if_flags; /* per-fork flags */
|
||||
unsigned char if_ext_max; /* max # of extent records */
|
||||
xfs_extnum_t if_lastex; /* last if_extents used */
|
||||
union {
|
||||
xfs_bmbt_rec_host_t *if_extents;/* linear map file exts */
|
||||
xfs_ext_irec_t *if_ext_irec; /* irec map file exts */
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "xfs_mount.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_alloc.h"
|
||||
#include "xfs_discard.h"
|
||||
|
||||
/*
|
||||
* Perform initial CIL structure initialisation. If the CIL is not
|
||||
@ -361,18 +362,28 @@ xlog_cil_committed(
|
||||
int abort)
|
||||
{
|
||||
struct xfs_cil_ctx *ctx = args;
|
||||
struct xfs_mount *mp = ctx->cil->xc_log->l_mp;
|
||||
|
||||
xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain,
|
||||
ctx->start_lsn, abort);
|
||||
|
||||
xfs_alloc_busy_sort(&ctx->busy_extents);
|
||||
xfs_alloc_busy_clear(ctx->cil->xc_log->l_mp, &ctx->busy_extents);
|
||||
xfs_alloc_busy_clear(mp, &ctx->busy_extents,
|
||||
(mp->m_flags & XFS_MOUNT_DISCARD) && !abort);
|
||||
|
||||
spin_lock(&ctx->cil->xc_cil_lock);
|
||||
list_del(&ctx->committing);
|
||||
spin_unlock(&ctx->cil->xc_cil_lock);
|
||||
|
||||
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_alloc_busy_clear(mp, &ctx->busy_extents, false);
|
||||
}
|
||||
|
||||
kmem_free(ctx);
|
||||
}
|
||||
|
||||
|
@ -224,6 +224,7 @@ typedef struct xfs_mount {
|
||||
#define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem
|
||||
operations, typically for
|
||||
disk errors in metadata */
|
||||
#define XFS_MOUNT_DISCARD (1ULL << 5) /* discard unused blocks */
|
||||
#define XFS_MOUNT_RETERR (1ULL << 6) /* return alignment errors to
|
||||
user */
|
||||
#define XFS_MOUNT_NOALIGN (1ULL << 7) /* turn off stripe alignment
|
||||
|
@ -609,7 +609,7 @@ xfs_trans_free(
|
||||
struct xfs_trans *tp)
|
||||
{
|
||||
xfs_alloc_busy_sort(&tp->t_busy);
|
||||
xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy);
|
||||
xfs_alloc_busy_clear(tp->t_mountp, &tp->t_busy, false);
|
||||
|
||||
atomic_dec(&tp->t_mountp->m_active_trans);
|
||||
xfs_trans_free_dqinfo(tp);
|
||||
|
Loading…
Reference in New Issue
Block a user