Commit d484467c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-4.12-merge-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs updates from Darrick Wong:
 "Here are the XFS changes for 4.12. The big new feature for this
  release is the new space mapping ioctl that we've been discussing
  since LSF2016, but other than that most of the patches are larger bug
  fixes, memory corruption prevention, and other cleanups.

  Summary:
   - various code cleanups
   - introduce GETFSMAP ioctl
   - various refactoring
   - avoid dio reads past eof
   - fix memory corruption and other errors with fragmented directory blocks
   - fix accidental userspace memory corruptions
   - publish fs uuid in superblock
   - make fstrim terminatable
   - fix race between quotaoff and in-core inode creation
   - avoid use-after-free when finishing up w/ buffer heads
   - reserve enough space to handle bmap tree resizing during cow remap"

* tag 'xfs-4.12-merge-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (53 commits)
  xfs: fix use-after-free in xfs_finish_page_writeback
  xfs: reserve enough blocks to handle btree splits when remapping
  xfs: wait on new inodes during quotaoff dquot release
  xfs: update ag iterator to support wait on new inodes
  xfs: support ability to wait on new inodes
  xfs: publish UUID in struct super_block
  xfs: Allow user to kill fstrim process
  xfs: better log intent item refcount checking
  xfs: fix up quotacheck buffer list error handling
  xfs: remove xfs_trans_ail_delete_bulk
  xfs: don't use bool values in trace buffers
  xfs: fix getfsmap userspace memory corruption while setting OF_LAST
  xfs: fix __user annotations for xfs_ioc_getfsmap
  xfs: corruption needs to respect endianess too!
  xfs: use NULL instead of 0 to initialize a pointer in xfs_ioc_getfsmap
  xfs: use NULL instead of 0 to initialize a pointer in xfs_getfsmap
  xfs: simplify validation of the unwritten extent bit
  xfs: remove unused values from xfs_exntst_t
  xfs: remove the unused XFS_MAXLINK_1 define
  xfs: more do_div cleanups
  ...
parents 044f1daa 161f55ef
......@@ -910,6 +910,9 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
break;
}
pos += ret;
if (iov_iter_rw(iter) == READ && pos >= dio->i_size)
break;
} while ((count = iov_iter_count(iter)) > 0);
blk_finish_plug(&plug);
......
......@@ -79,6 +79,7 @@ xfs-y += xfs_aops.o \
xfs_extent_busy.o \
xfs_file.o \
xfs_filestream.o \
xfs_fsmap.o \
xfs_fsops.o \
xfs_globals.o \
xfs_icache.o \
......
......@@ -2868,3 +2868,60 @@ xfs_free_extent(
xfs_trans_brelse(tp, agbp);
return error;
}
struct xfs_alloc_query_range_info {
xfs_alloc_query_range_fn fn;
void *priv;
};
/* Format btree record and pass to our callback. */
STATIC int
xfs_alloc_query_range_helper(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec,
void *priv)
{
struct xfs_alloc_query_range_info *query = priv;
struct xfs_alloc_rec_incore irec;
irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
return query->fn(cur, &irec, query->priv);
}
/* Find all free space within a given range of blocks. */
int
xfs_alloc_query_range(
struct xfs_btree_cur *cur,
struct xfs_alloc_rec_incore *low_rec,
struct xfs_alloc_rec_incore *high_rec,
xfs_alloc_query_range_fn fn,
void *priv)
{
union xfs_btree_irec low_brec;
union xfs_btree_irec high_brec;
struct xfs_alloc_query_range_info query;
ASSERT(cur->bc_btnum == XFS_BTNUM_BNO);
low_brec.a = *low_rec;
high_brec.a = *high_rec;
query.priv = priv;
query.fn = fn;
return xfs_btree_query_range(cur, &low_brec, &high_brec,
xfs_alloc_query_range_helper, &query);
}
/* Find all free space records. */
int
xfs_alloc_query_all(
struct xfs_btree_cur *cur,
xfs_alloc_query_range_fn fn,
void *priv)
{
struct xfs_alloc_query_range_info query;
ASSERT(cur->bc_btnum == XFS_BTNUM_BNO);
query.priv = priv;
query.fn = fn;
return xfs_btree_query_all(cur, xfs_alloc_query_range_helper, &query);
}
......@@ -219,4 +219,16 @@ int xfs_free_extent_fix_freelist(struct xfs_trans *tp, xfs_agnumber_t agno,
xfs_extlen_t xfs_prealloc_blocks(struct xfs_mount *mp);
typedef int (*xfs_alloc_query_range_fn)(
struct xfs_btree_cur *cur,
struct xfs_alloc_rec_incore *rec,
void *priv);
int xfs_alloc_query_range(struct xfs_btree_cur *cur,
struct xfs_alloc_rec_incore *low_rec,
struct xfs_alloc_rec_incore *high_rec,
xfs_alloc_query_range_fn fn, void *priv);
int xfs_alloc_query_all(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn,
void *priv);
#endif /* __XFS_ALLOC_H__ */
......@@ -205,19 +205,37 @@ xfs_allocbt_init_key_from_rec(
union xfs_btree_key *key,
union xfs_btree_rec *rec)
{
ASSERT(rec->alloc.ar_startblock != 0);
key->alloc.ar_startblock = rec->alloc.ar_startblock;
key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
}
STATIC void
xfs_bnobt_init_high_key_from_rec(
union xfs_btree_key *key,
union xfs_btree_rec *rec)
{
__u32 x;
x = be32_to_cpu(rec->alloc.ar_startblock);
x += be32_to_cpu(rec->alloc.ar_blockcount) - 1;
key->alloc.ar_startblock = cpu_to_be32(x);
key->alloc.ar_blockcount = 0;
}
STATIC void
xfs_cntbt_init_high_key_from_rec(
union xfs_btree_key *key,
union xfs_btree_rec *rec)
{
key->alloc.ar_blockcount = rec->alloc.ar_blockcount;
key->alloc.ar_startblock = 0;
}
STATIC void
xfs_allocbt_init_rec_from_cur(
struct xfs_btree_cur *cur,
union xfs_btree_rec *rec)
{
ASSERT(cur->bc_rec.a.ar_startblock != 0);
rec->alloc.ar_startblock = cpu_to_be32(cur->bc_rec.a.ar_startblock);
rec->alloc.ar_blockcount = cpu_to_be32(cur->bc_rec.a.ar_blockcount);
}
......@@ -236,18 +254,24 @@ xfs_allocbt_init_ptr_from_cur(
}
STATIC __int64_t
xfs_allocbt_key_diff(
xfs_bnobt_key_diff(
struct xfs_btree_cur *cur,
union xfs_btree_key *key)
{
xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
xfs_alloc_key_t *kp = &key->alloc;
__int64_t diff;
if (cur->bc_btnum == XFS_BTNUM_BNO) {
return (__int64_t)be32_to_cpu(kp->ar_startblock) -
rec->ar_startblock;
}
return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
}
STATIC __int64_t
xfs_cntbt_key_diff(
struct xfs_btree_cur *cur,
union xfs_btree_key *key)
{
xfs_alloc_rec_incore_t *rec = &cur->bc_rec.a;
xfs_alloc_key_t *kp = &key->alloc;
__int64_t diff;
diff = (__int64_t)be32_to_cpu(kp->ar_blockcount) - rec->ar_blockcount;
if (diff)
......@@ -256,6 +280,33 @@ xfs_allocbt_key_diff(
return (__int64_t)be32_to_cpu(kp->ar_startblock) - rec->ar_startblock;
}
STATIC __int64_t
xfs_bnobt_diff_two_keys(
struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
union xfs_btree_key *k2)
{
return (__int64_t)be32_to_cpu(k1->alloc.ar_startblock) -
be32_to_cpu(k2->alloc.ar_startblock);
}
STATIC __int64_t
xfs_cntbt_diff_two_keys(
struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
union xfs_btree_key *k2)
{
__int64_t diff;
diff = be32_to_cpu(k1->alloc.ar_blockcount) -
be32_to_cpu(k2->alloc.ar_blockcount);
if (diff)
return diff;
return be32_to_cpu(k1->alloc.ar_startblock) -
be32_to_cpu(k2->alloc.ar_startblock);
}
static bool
xfs_allocbt_verify(
struct xfs_buf *bp)
......@@ -346,44 +397,54 @@ const struct xfs_buf_ops xfs_allocbt_buf_ops = {
#if defined(DEBUG) || defined(XFS_WARN)
STATIC int
xfs_allocbt_keys_inorder(
xfs_bnobt_keys_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
union xfs_btree_key *k2)
{
if (cur->bc_btnum == XFS_BTNUM_BNO) {
return be32_to_cpu(k1->alloc.ar_startblock) <
be32_to_cpu(k2->alloc.ar_startblock);
} else {
return be32_to_cpu(k1->alloc.ar_blockcount) <
be32_to_cpu(k2->alloc.ar_blockcount) ||
(k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
be32_to_cpu(k1->alloc.ar_startblock) <
be32_to_cpu(k2->alloc.ar_startblock));
}
return be32_to_cpu(k1->alloc.ar_startblock) <
be32_to_cpu(k2->alloc.ar_startblock);
}
STATIC int
xfs_allocbt_recs_inorder(
xfs_bnobt_recs_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_rec *r1,
union xfs_btree_rec *r2)
{
if (cur->bc_btnum == XFS_BTNUM_BNO) {
return be32_to_cpu(r1->alloc.ar_startblock) +
be32_to_cpu(r1->alloc.ar_blockcount) <=
be32_to_cpu(r2->alloc.ar_startblock);
} else {
return be32_to_cpu(r1->alloc.ar_blockcount) <
be32_to_cpu(r2->alloc.ar_blockcount) ||
(r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
be32_to_cpu(r1->alloc.ar_startblock) <
be32_to_cpu(r2->alloc.ar_startblock));
}
return be32_to_cpu(r1->alloc.ar_startblock) +
be32_to_cpu(r1->alloc.ar_blockcount) <=
be32_to_cpu(r2->alloc.ar_startblock);
}
STATIC int
xfs_cntbt_keys_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_key *k1,
union xfs_btree_key *k2)
{
return be32_to_cpu(k1->alloc.ar_blockcount) <
be32_to_cpu(k2->alloc.ar_blockcount) ||
(k1->alloc.ar_blockcount == k2->alloc.ar_blockcount &&
be32_to_cpu(k1->alloc.ar_startblock) <
be32_to_cpu(k2->alloc.ar_startblock));
}
#endif /* DEBUG */
static const struct xfs_btree_ops xfs_allocbt_ops = {
STATIC int
xfs_cntbt_recs_inorder(
struct xfs_btree_cur *cur,
union xfs_btree_rec *r1,
union xfs_btree_rec *r2)
{
return be32_to_cpu(r1->alloc.ar_blockcount) <
be32_to_cpu(r2->alloc.ar_blockcount) ||
(r1->alloc.ar_blockcount == r2->alloc.ar_blockcount &&
be32_to_cpu(r1->alloc.ar_startblock) <
be32_to_cpu(r2->alloc.ar_startblock));
}
#endif /* DEBUG */
static const struct xfs_btree_ops xfs_bnobt_ops = {
.rec_len = sizeof(xfs_alloc_rec_t),
.key_len = sizeof(xfs_alloc_key_t),
......@@ -395,13 +456,39 @@ static const struct xfs_btree_ops xfs_allocbt_ops = {
.get_minrecs = xfs_allocbt_get_minrecs,
.get_maxrecs = xfs_allocbt_get_maxrecs,
.init_key_from_rec = xfs_allocbt_init_key_from_rec,
.init_high_key_from_rec = xfs_bnobt_init_high_key_from_rec,
.init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
.init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
.key_diff = xfs_allocbt_key_diff,
.key_diff = xfs_bnobt_key_diff,
.buf_ops = &xfs_allocbt_buf_ops,
.diff_two_keys = xfs_bnobt_diff_two_keys,
#if defined(DEBUG) || defined(XFS_WARN)
.keys_inorder = xfs_allocbt_keys_inorder,
.recs_inorder = xfs_allocbt_recs_inorder,
.keys_inorder = xfs_bnobt_keys_inorder,
.recs_inorder = xfs_bnobt_recs_inorder,
#endif
};
static const struct xfs_btree_ops xfs_cntbt_ops = {
.rec_len = sizeof(xfs_alloc_rec_t),
.key_len = sizeof(xfs_alloc_key_t),
.dup_cursor = xfs_allocbt_dup_cursor,
.set_root = xfs_allocbt_set_root,
.alloc_block = xfs_allocbt_alloc_block,
.free_block = xfs_allocbt_free_block,
.update_lastrec = xfs_allocbt_update_lastrec,
.get_minrecs = xfs_allocbt_get_minrecs,
.get_maxrecs = xfs_allocbt_get_maxrecs,
.init_key_from_rec = xfs_allocbt_init_key_from_rec,
.init_high_key_from_rec = xfs_cntbt_init_high_key_from_rec,
.init_rec_from_cur = xfs_allocbt_init_rec_from_cur,
.init_ptr_from_cur = xfs_allocbt_init_ptr_from_cur,
.key_diff = xfs_cntbt_key_diff,
.buf_ops = &xfs_allocbt_buf_ops,
.diff_two_keys = xfs_cntbt_diff_two_keys,
#if defined(DEBUG) || defined(XFS_WARN)
.keys_inorder = xfs_cntbt_keys_inorder,
.recs_inorder = xfs_cntbt_recs_inorder,
#endif
};
......@@ -427,16 +514,15 @@ xfs_allocbt_init_cursor(
cur->bc_mp = mp;
cur->bc_btnum = btnum;
cur->bc_blocklog = mp->m_sb.sb_blocklog;
cur->bc_ops = &xfs_allocbt_ops;
if (btnum == XFS_BTNUM_BNO)
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtb_2);
else
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtc_2);
if (btnum == XFS_BTNUM_CNT) {
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtc_2);
cur->bc_ops = &xfs_cntbt_ops;
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]);
cur->bc_flags = XFS_BTREE_LASTREC_UPDATE;
} else {
cur->bc_statoff = XFS_STATS_CALC_INDEX(xs_abtb_2);
cur->bc_ops = &xfs_bnobt_ops;
cur->bc_nlevels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
}
......
This diff is collapsed.
......@@ -171,6 +171,18 @@ static inline int xfs_bmapi_whichfork(int bmapi_flags)
{ BMAP_COWFORK, "COW" }
/*
* Return true if the extent is a real, allocated extent, or false if it is a
* delayed allocation, and unwritten extent or a hole.
*/
static inline bool xfs_bmap_is_real_extent(struct xfs_bmbt_irec *irec)
{
return irec->br_state != XFS_EXT_UNWRITTEN &&
irec->br_startblock != HOLESTARTBLOCK &&
irec->br_startblock != DELAYSTARTBLOCK &&
!isnullstartblock(irec->br_startblock);
}
/*
* This macro is used to determine how many extents will be shifted
* in one write transaction. We could require two splits,
......@@ -232,8 +244,6 @@ int xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
struct xfs_bmbt_irec *del);
void xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx,
struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *del);
int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
xfs_extnum_t num);
uint xfs_default_attroffset(struct xfs_inode *ip);
int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
......
......@@ -366,32 +366,6 @@ xfs_bmbt_to_bmdr(
memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
}
/*
* Check extent records, which have just been read, for
* any bit in the extent flag field. ASSERT on debug
* kernels, as this condition should not occur.
* Return an error condition (1) if any flags found,
* otherwise return 0.
*/
int
xfs_check_nostate_extents(
xfs_ifork_t *ifp,
xfs_extnum_t idx,
xfs_extnum_t num)
{
for (; num > 0; num--, idx++) {
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, idx);
if ((ep->l0 >>
(64 - BMBT_EXNTFLAG_BITLEN)) != 0) {
ASSERT(0);
return 1;
}
}
return 0;
}
STATIC struct xfs_btree_cur *
xfs_bmbt_dup_cursor(
struct xfs_btree_cur *cur)
......@@ -448,7 +422,6 @@ xfs_bmbt_alloc_block(
if (args.fsbno == NULLFSBLOCK) {
args.fsbno = be64_to_cpu(start->l);
args.type = XFS_ALLOCTYPE_START_BNO;
try_another_ag:
/*
* Make sure there is sufficient room left in the AG to
* complete a full tree split for an extent insert. If
......@@ -477,22 +450,6 @@ xfs_bmbt_alloc_block(
if (error)
goto error0;
/*
* During a CoW operation, the allocation and bmbt updates occur in
* different transactions. The mapping code tries to put new bmbt
* blocks near extents being mapped, but the only way to guarantee this
* is if the alloc and the mapping happen in a single transaction that
* has a block reservation. That isn't the case here, so if we run out
* of space we'll try again with another AG.
*/
if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
args.fsbno == NULLFSBLOCK &&
args.type == XFS_ALLOCTYPE_NEAR_BNO) {
args.fsbno = cur->bc_private.b.firstblock;
args.type = XFS_ALLOCTYPE_FIRST_AG;
goto try_another_ag;
}
if (args.fsbno == NULLFSBLOCK && args.minleft) {
/*
* Could not find an AG with enough free space to satisfy
......
......@@ -24,14 +24,6 @@ struct xfs_mount;
struct xfs_inode;
struct xfs_trans;
/*
* Extent state and extent format macros.
*/
#define XFS_EXTFMT_INODE(x) \
(xfs_sb_version_hasextflgbit(&((x)->i_mount->m_sb)) ? \
XFS_EXTFMT_HASSTATE : XFS_EXTFMT_NOSTATE)
#define ISUNWRITTEN(x) ((x)->br_state == XFS_EXT_UNWRITTEN)
/*
* Btree block header size depends on a superblock flag.
*/
......@@ -140,4 +132,18 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
struct xfs_trans *, struct xfs_inode *, int);
/*
* Check that the extent does not contain an invalid unwritten extent flag.
*/
static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
struct xfs_bmbt_rec_host *ep)
{
if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
return true;
if (whichfork == XFS_DATA_FORK &&
xfs_sb_version_hasextflgbit(&mp->m_sb))
return true;
return false;
}
#endif /* __XFS_BMAP_BTREE_H__ */
......@@ -4842,6 +4842,21 @@ xfs_btree_query_range(
fn, priv);
}
/* Query a btree for all records. */
int
xfs_btree_query_all(
struct xfs_btree_cur *cur,
xfs_btree_query_range_fn fn,
void *priv)
{
union xfs_btree_irec low_rec;
union xfs_btree_irec high_rec;
memset(&low_rec, 0, sizeof(low_rec));
memset(&high_rec, 0xFF, sizeof(high_rec));
return xfs_btree_query_range(cur, &low_rec, &high_rec, fn, priv);
}
/*
* Calculate the number of blocks needed to store a given number of records
* in a short-format (per-AG metadata) btree.
......
......@@ -496,6 +496,8 @@ typedef int (*xfs_btree_query_range_fn)(struct xfs_btree_cur *cur,
int xfs_btree_query_range(struct xfs_btree_cur *cur,
union xfs_btree_irec *low_rec, union xfs_btree_irec *high_rec,
xfs_btree_query_range_fn fn, void *priv);
int xfs_btree_query_all(struct xfs_btree_cur *cur, xfs_btree_query_range_fn fn,
void *priv);
typedef int (*xfs_btree_visit_blocks_fn)(struct xfs_btree_cur *cur, int level,
void *data);
......
......@@ -35,13 +35,8 @@ int
xfs_calc_dquots_per_chunk(
unsigned int nbblks) /* basic block units */
{
unsigned int ndquots;
ASSERT(nbblks > 0);
ndquots = BBTOB(nbblks);
do_div(ndquots, sizeof(xfs_dqblk_t));
return ndquots;
return BBTOB(nbblks) / sizeof(xfs_dqblk_t);
}
/*
......
......@@ -930,10 +930,8 @@ static inline uint xfs_dinode_size(int version)
/*
* The 32 bit link count in the inode theoretically maxes out at UINT_MAX.
* Since the pathconf interface is signed, we use 2^31 - 1 instead.
* The old inode format had a 16 bit link count, so its maximum is USHRT_MAX.
*/
#define XFS_MAXLINK ((1U << 31) - 1U)
#define XFS_MAXLINK_1 65535U
/*
* Values for di_format
......@@ -1577,20 +1575,11 @@ static inline xfs_filblks_t startblockval(xfs_fsblock_t x)
return (xfs_filblks_t)((x) & ~STARTBLOCKMASK);
}
/*
* Possible extent formats.
*/
typedef enum {
XFS_EXTFMT_NOSTATE = 0,
XFS_EXTFMT_HASSTATE
} xfs_exntfmt_t;
/*
* Possible extent states.
*/
typedef enum {
XFS_EXT_NORM, XFS_EXT_UNWRITTEN,
XFS_EXT_DMAPI_OFFLINE, XFS_EXT_INVALID
} xfs_exntst_t;
/*
......
......@@ -92,6 +92,18 @@ struct getbmapx {
#define BMV_OF_LAST 0x4 /* segment is the last in the file */
#define BMV_OF_SHARED 0x8 /* segment shared with another file */
/* fmr_owner special values for FS_IOC_GETFSMAP */
#define XFS_FMR_OWN_FREE FMR_OWN_FREE /* free space */
#define XFS_FMR_OWN_UNKNOWN FMR_OWN_UNKNOWN /* unknown owner */
#define XFS_FMR_OWN_FS FMR_OWNER('X', 1) /* static fs metadata */
#define XFS_FMR_OWN_LOG FMR_OWNER('X', 2) /* journalling log */
#define XFS_FMR_OWN_AG FMR_OWNER('X', 3) /* per-AG metadata */
#define XFS_FMR_OWN_INOBT FMR_OWNER('X', 4) /* inode btree blocks */
#define XFS_FMR_OWN_INODES FMR_OWNER('X', 5) /* inodes */
#define XFS_FMR_OWN_REFC FMR_OWNER('X', 6) /* refcount tree */
#define XFS_FMR_OWN_COW FMR_OWNER('X', 7) /* cow staging */
#define XFS_FMR_OWN_DEFECTIVE FMR_OWNER('X', 8) /* bad blocks */
/*
* Structure for XFS_IOC_FSSETDM.
* For use by backup and restore programs to set the XFS on-disk inode
......@@ -502,6 +514,7 @@ typedef struct xfs_swapext
#define XFS_IOC_GETBMAPX _IOWR('X', 56, struct getbmap)
#define XFS_IOC_ZERO_RANGE _IOW ('X', 57, struct xfs_flock64)
#define XFS_IOC_FREE_EOFBLOCKS _IOR ('X', 58, struct xfs_fs_eofblocks)
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
/*
* ioctl commands that replace IRIX syssgi()'s
......
......@@ -508,7 +508,7 @@ xfs_iread(
/* even unallocated inodes are verified */
if (!xfs_dinode_verify(mp, ip->i_ino, dip)) {
xfs_alert(mp, "%s: validation failed for inode %lld failed",
xfs_alert(mp, "%s: validation failed for inode %lld",
__func__, ip->i_ino);
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, dip);
......
......@@ -42,35 +42,6 @@ STATIC int xfs_iformat_local(xfs_inode_t *, xfs_dinode_t *, int, int);
STATIC int xfs_iformat_extents(xfs_inode_t *, xfs_dinode_t *, int);
STATIC int xfs_iformat_btree(xfs_inode_t *, xfs_dinode_t *, int);
#ifdef DEBUG
/*
* Make sure that the extents in the given memory buffer
* are valid.
*/
void
xfs_validate_extents(
xfs_ifork_t *ifp,
int nrecs,
xfs_exntfmt_t fmt)
{
xfs_bmbt_irec_t irec;
xfs_bmbt_rec_host_t rec;
int i;
for (i = 0; i < nrecs; i++) {
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
rec.l0 = get_unaligned(&ep->l0);
rec.l1 = get_unaligned(&ep->l1);
xfs_bmbt_get_all(&rec, &irec);
if (fmt == XFS_EXTFMT_NOSTATE)
ASSERT(irec.br_state == XFS_EXT_NORM);
}
}
#else /* DEBUG */
#define xfs_validate_extents(ifp, nrecs, fmt)
#endif /* DEBUG */
/*
* Move inode type and inode format specific information from the
* on-disk inode to the in-core inode. For fifos, devs, and sockets
......@@ -352,40 +323,33 @@ xfs_iformat_local(
}
/*
* The file consists of a set of extents all
* of which fit into the on-disk inode.
* If there are few enough extents to fit into
* the if_inline_ext, then copy them there.
* Otherwise allocate a buffer for them and copy
* them into it. Either way, set if_extents
* to point at the extents.
* The file consists of a set of extents all of which fit into the on-disk
* inode. If there are few enough extents to fit into the if_inline_ext, then
* copy them there. Otherwise allocate a buffer for them and copy them into it.
* Either way, set if_extents to point at the extents.
*/
STATIC int
xfs_iformat_extents(
xfs_inode_t *ip,
xfs_dinode_t *dip,
int whichfork)
struct xfs_inode *ip,
struct xfs_dinode *dip,
int whichfork)
{
xfs_bmbt_rec_t *dp;
xfs_ifork_t *ifp;
int nex;
int size;
int i;
ifp = XFS_IFORK_PTR(ip, whichfork);
nex = XFS_DFORK_NEXTENTS(dip, whichfork);
size = nex * (uint)sizeof(xfs_bmbt_rec_t);
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
int nex = XFS_DFORK_NEXTENTS(dip, whichfork);
int size = nex * sizeof(xfs_bmbt_rec_t);
struct xfs_bmbt_rec *dp;
int i;
/*
* If the number of extents is unreasonable, then something
* is wrong and we just bail out rather than crash in
* kmem_alloc() or memcpy() below.
* If the number of extents is unreasonable, then something is wrong and
* we just bail out rather than crash in kmem_alloc() or memcpy() below.
*/
if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
xfs_warn(ip->i_mount, "corrupt inode %Lu ((a)extents = %d).",
(unsigned long long) ip->i_ino, nex);
XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
ip->i_mount, dip);
mp, dip);
return -EFSCORRUPTED;
}
......@@ -400,22 +364,17 @@ xfs_iformat_extents(
ifp->if_bytes = size;
if (size) {
dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
xfs_validate_extents(ifp, nex, XFS_EXTFMT_INODE(ip));
for (i = 0; i < nex; i++, dp++) {
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
ep->l0 = get_unaligned_be64(&dp->l0);
ep->l1 = get_unaligned_be64(&dp->l1);
if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
XFS_ERROR_REPORT("xfs_iformat_extents(2)",
XFS_ERRLEVEL_LOW, mp);
return -EFSCORRUPTED;
}
}
XFS_BMAP_TRACE_EXLIST(ip, nex, whichfork);
if (whichfork != XFS_DATA_FORK ||
XFS_EXTFMT_INODE(ip) == XFS_EXTFMT_NOSTATE)
if (unlikely(xfs_check_nostate_extents(
ifp, 0, nex))) {
XFS_ERROR_REPORT("xfs_iformat_extents(2)",
XFS_ERRLEVEL_LOW,
ip->i_mount);
return -EFSCORRUPTED;
}
}
ifp->if_flags |= XFS_IFEXTENTS;
return 0;
......@@ -518,7 +477,6 @@ xfs_iread_extents(
xfs_iext_destroy(ifp);
return error;
}
xfs_validate_extents(ifp, nextents, XFS_EXTFMT_INODE(ip));
ifp->if_flags |= XFS_IFEXTENTS;
return 0;
}
......@@ -837,6 +795,9 @@ xfs_iextents_copy(
copied = 0;
for (i = 0; i < nrecs; i++) {
xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));
start_block = xfs_bmbt_get_startblock(ep);
if (isnullstartblock(start_block)) {
/*
......@@ -852,7 +813,6 @@ xfs_iextents_copy(
copied++;
}
ASSERT(copied != 0);
xfs_validate_extents(ifp, copied, XFS_EXTFMT_INODE(ip));
return (copied * (uint)sizeof(xfs_bmbt_rec_t));
}
......
......@@ -2001,14 +2001,14 @@ xfs_rmap_query_range_helper(
/* Find all rmaps between two keys. */
int
xfs_rmap_query_range(
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *low_rec,
struct xfs_rmap_irec *high_rec,
xfs_rmap_query_range_fn fn,
void *priv)
struct xfs_btree_cur *cur,
struct xfs_rmap_irec *low_rec,
struct xfs_rmap_irec *high_rec,
xfs_rmap_query_range_fn fn,
void *priv)
{
union xfs_btree_irec low_brec;
union xfs_btree_irec high_brec;
union xfs_btree_irec low_brec;
union xfs_btree_irec high_brec;
struct xfs_rmap_query_range_info query;
low_brec.r = *low_rec;
......@@ -2019,6 +2019,20 @@ xfs_rmap_query_range(
xfs_rmap_query_range_helper, &query);
}
/* Find all rmaps. */
int
xfs_rmap_query_all(
struct xfs_btree_cur *cur,
xfs_rmap_query_range_fn fn,
void *priv)
{
struct xfs_rmap_query_range_info query;
query.priv = priv;
query.fn = fn;
return xfs_btree_query_all(cur, xfs_rmap_query_range_helper, &query);
}
/* Clean up after calling xfs_rmap_finish_one. */
void
xfs_rmap_finish_one_cleanup(
......@@ -2291,3 +2305,31 @@ xfs_rmap_free_extent(
return __xfs_rmap_add(mp, dfops, XFS_RMAP_FREE, owner,
XFS_DATA_FORK, &bmap);
}
/* Compare rmap records. Returns -1 if a < b, 1 if a > b, and 0 if equal. */
int
xfs_rmap_compare(
const struct xfs_rmap_irec *a,
const struct xfs_rmap_irec *b)
{
__u64 oa;
__u64 ob;
oa = xfs_rmap_irec_offset_pack(a);
ob = xfs_rmap_irec_offset_pack(b);
if (a->rm_startblock < b->rm_startblock)
return -1;
else if (a->rm_startblock > b->rm_startblock)
return 1;
else if (a->rm_owner < b->rm_owner)
return -1;
else if (a->rm_owner > b->rm_owner)
return 1;
else if (oa < ob)
return -1;
else if (oa > ob)
return 1;
else
return 0;
}
......@@ -162,6 +162,8 @@ typedef int (*xfs_rmap_query_range_fn)(
int xfs_rmap_query_range(struct xfs_btree_cur *cur,
struct xfs_rmap_irec *low_rec, struct xfs_rmap_irec *high_rec,
xfs_rmap_query_range_fn fn, void *priv);
int xfs_rmap_query_all(struct xfs_btree_cur *cur, xfs_rmap_query_range_fn fn,
void *priv);
enum xfs_rmap_intent_type {
XFS_RMAP_MAP,
......@@ -212,5 +214,7 @@ int xfs_rmap_find_left_neighbor(struct xfs_btree_cur *cur, xfs_agblock_t bno,
int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
uint64_t owner, uint64_t offset, unsigned int flags,
struct xfs_rmap_irec *irec, int *stat);
int xfs_rmap_compare(const struct xfs_rmap_irec *a,
const struct xfs_rmap_irec *b);
#endif /* __XFS_RMAP_H__ */
......@@ -1016,3 +1016,73 @@ xfs_rtfree_extent(
}
return 0;
}
/* Find all the free records within a given range. */
int
xfs_rtalloc_query_range(
struct xfs_trans *tp,
struct xfs_rtalloc_rec *low_rec,
struct xfs_rtalloc_rec *high_rec,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_rtalloc_rec rec;
struct xfs_mount *mp = tp->t_mountp;
xfs_rtblock_t rtstart;
xfs_rtblock_t rtend;
xfs_rtblock_t rem;
int is_free;
int error = 0;
if (low_rec->ar_startblock > high_rec->ar_startblock)
return -EINVAL;
else if (low_rec->ar_startblock == high_rec->ar_startblock)
return 0;
/* Iterate the bitmap, looking for discrepancies. */
rtstart = low_rec->ar_startblock;
rem = high_rec->ar_startblock - rtstart;
while (rem) {
/* Is the first block free? */
error = xfs_rtcheck_range(mp, tp, rtstart, 1, 1, &rtend,
&is_free);
if (error)
break;
/* How long does the extent go for? */
error = xfs_rtfind_forw(mp, tp, rtstart,
high_rec->ar_startblock - 1, &rtend);
if (error)
break;
if (is_free) {
rec.ar_startblock = rtstart;
rec.ar_blockcount = rtend - rtstart + 1;
error = fn(tp, &rec, priv);
if (error)
break;
}
rem -= rtend - rtstart + 1;
rtstart = rtend + 1;
}
return error;
}
/* Find all the free records. */
int
xfs_rtalloc_query_all(
struct xfs_trans *tp,
xfs_rtalloc_query_range_fn fn,
void *priv)
{
struct xfs_rtalloc_rec keys[2];
keys[0].ar_startblock = 0;
keys[1].ar_startblock = tp->t_mountp->m_sb.sb_rblocks;
keys[0].ar_blockcount = keys[1].ar_blockcount = 0;
return xfs_rtalloc_query_range(tp, &keys[0], &keys[1], fn, priv);
}
......@@ -21,8 +21,20 @@
/*
* Components of space reservations.
*/
/* Worst case number of rmaps that can be held in a block. */
#define XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) \
(((mp)->m_rmap_mxr[0]) - ((mp)->m_rmap_mnr[0]))
/* Adding one rmap could split every level up to the top of the tree. */
#define XFS_RMAPADD_SPACE_RES(mp) ((mp)->m_rmap_maxlevels)
/* Blocks we might need to add "b" rmaps to a tree. */
#define XFS_NRMAPADD_SPACE_RES(mp, b)\
(((b + XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) - 1) / \
XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) * \
XFS_RMAPADD_SPACE_RES(mp))
#define XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) \
(((mp)->m_alloc_mxr[0]) - ((mp)->m_alloc_mnr[0]))
#define XFS_EXTENTADD_SPACE_RES(mp,w) (XFS_BM_MAXLEVELS(mp,w) - 1)
......@@ -30,13 +42,12 @@
(((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
XFS_EXTENTADD_SPACE_RES(mp,w))
/* Blocks we might need to add "b" mappings & rmappings to a file. */
#define XFS_SWAP_RMAP_SPACE_RES(mp,b,w)\
(((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
XFS_EXTENTADD_SPACE_RES(mp,w) + \
((b + XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) - 1) / \
XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) * \
(mp)->m_rmap_maxlevels)
(XFS_NEXTENTADD_SPACE_RES((mp), (b), (w)) + \
XFS_NRMAPADD_SPACE_RES((mp), (b)))
#define XFS_DAENTER_1B(mp,w) \
((w) == XFS_DATA_FORK ? (mp)->m_dir_geo->fsbcount : 1)
#define XFS_DAENTER_DBS(mp,w) \
......
......@@ -111,11 +111,11 @@ xfs_finish_page_writeback(
bsize = bh->b_size;
do {
if (off > end)
break;
next = bh->b_this_page;
if (off < bvec->bv_offset)
goto next_bh;
if (off > end)
break;
bh->b_end_io(bh, !error);
next_bh:
off += bsize;
......@@ -1261,8 +1261,8 @@ xfs_get_blocks(
if (nimaps) {
trace_xfs_get_blocks_found(ip, offset, size,
ISUNWRITTEN(&imap) ? XFS_IO_UNWRITTEN
: XFS_IO_OVERWRITE, &imap);
imap.br_state == XFS_EXT_UNWRITTEN ?
XFS_IO_UNWRITTEN : XFS_IO_OVERWRITE, &imap);
xfs_iunlock(ip, lockmode);
} else {
trace_xfs_get_blocks_notfound(ip, offset, size);
......@@ -1276,9 +1276,7 @@ xfs_get_blocks(
* For unwritten extents do not report a disk address in the buffered
* read case (treat as if we're reading into a hole).
*/
if (imap.br_startblock != HOLESTARTBLOCK &&
imap.br_startblock != DELAYSTARTBLOCK &&
!ISUNWRITTEN(&imap))
if (xfs_bmap_is_real_extent(&imap))
xfs_map_buffer(inode, bh_result, &imap, offset);
/*
......
......@@ -34,6 +34,8 @@
#include "xfs_bmap.h"
#include "xfs_icache.h"
#include "xfs_trace.h"
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
kmem_zone_t *xfs_bui_zone;
......@@ -215,6 +217,7 @@ void
xfs_bui_release(
struct xfs_bui_log_item *buip)
{
ASSERT(atomic_read(&buip->bui_refcount) > 0);
if (atomic_dec_and_test(&buip->bui_refcount)) {
xfs_trans_ail_remove(&buip->bui_item, SHUTDOWN_LOG_IO_ERROR);
xfs_bui_item_free(buip);
......@@ -446,7 +449,8 @@ xfs_bui_recover(
return -EIO;
}
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate,
XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), 0, 0, &tp);
if (error)
return error;
budp = xfs_trans_get_bud(tp, buip);
......
......@@ -448,10 +448,9 @@ xfs_getbmap_adjust_shared(
next_map->br_blockcount = 0;
/* Only written data blocks can be shared. */
if (!xfs_is_reflink_inode(ip) || whichfork != XFS_DATA_FORK ||
map->br_startblock == DELAYSTARTBLOCK ||
map->br_startblock == HOLESTARTBLOCK ||
ISUNWRITTEN(map))
if (!xfs_is_reflink_inode(ip) ||
whichfork != XFS_DATA_FORK ||
!xfs_bmap_is_real_extent(map))
return 0;
agno = XFS_FSB_TO_AGNO(mp, map->br_startblock);
......@@ -904,9 +903,9 @@ xfs_can_free_eofblocks(struct xfs_inode *ip, bool force)
}
/*
* This is called by xfs_inactive to free any blocks beyond eof
* when the link count isn't zero and by xfs_dm_punch_hole() when
* punching a hole to EOF.
* This is called to free any blocks beyond eof. The caller must hold
* IOLOCK_EXCL unless we are in the inode reclaim path and have the only
* reference to the inode.
*/
int
xfs_free_eofblocks(
......@@ -921,8 +920,6 @@ xfs_free_eofblocks(
struct xfs_bmbt_irec imap;
struct xfs_mount *mp = ip->i_mount;
ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
/*
* Figure out if there are any blocks beyond the end
* of the file. If not, then there is nothing to do.
......@@ -1209,11 +1206,8 @@ xfs_adjust_extent_unmap_boundaries(
return error;
if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
xfs_daddr_t block;
ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
block = imap.br_startblock;
mod = do_div(block, mp->m_sb.sb_rextsize);
mod = do_mod(imap.br_startblock, mp->m_sb.sb_rextsize);
if (mod)
*startoffset_fsb += mp->m_sb.sb_rextsize - mod;
}
......
......@@ -1079,6 +1079,8 @@ void
xfs_buf_unlock(
struct xfs_buf *bp)
{
ASSERT(xfs_buf_islocked(bp));
XB_CLEAR_OWNER(bp);
up(&bp->b_sema);
......@@ -1814,6 +1816,28 @@ xfs_alloc_buftarg(
return NULL;
}
/*
* Cancel a delayed write list.
*
* Remove each buffer from the list, clear the delwri queue flag and drop the
* associated buffer reference.
*/
void
xfs_buf_delwri_cancel(
struct list_head *list)
{
struct xfs_buf *bp;
while (!list_empty(list)) {
bp = list_first_entry(list, struct xfs_buf, b_list);
xfs_buf_lock(bp);
bp->b_flags &= ~_XBF_DELWRI_Q;
list_del_init(&bp->b_list);
xfs_buf_relse(bp);
}
}
/*
* Add a buffer to the delayed write list.
*
......
......@@ -291,7 +291,6 @@ xfs_buf_readahead(
return xfs_buf_readahead_map(target, &map, 1, ops);
}
struct xfs_buf *xfs_buf_get_empty(struct xfs_buftarg *target, size_t numblks);
void xfs_buf_set_empty(struct xfs_buf *bp, size_t numblks);
int xfs_buf_associate_memory(struct xfs_buf *bp, void *mem, size_t length);
......@@ -330,6 +329,7 @@ extern void *xfs_buf_offset(struct xfs_buf *, size_t);
extern void xfs_buf_stale(struct xfs_buf *bp);
/* Delayed Write Buffer Routines */
extern void xfs_buf_delwri_cancel(struct list_head *);
extern bool xfs_buf_delwri_queue(struct xfs_buf *, struct list_head *);
extern int xfs_buf_delwri_submit(struct list_head *);
extern int xfs_buf_delwri_submit_nowait(struct list_head *);
......
......@@ -394,6 +394,7 @@ xfs_dir2_leaf_readbuf(
/*
* Do we need more readahead?
* Each loop tries to process 1 full dir blk; last may be partial.
*/
blk_start_plug(&plug);
for (mip->ra_index = mip->ra_offset = i = 0;
......@@ -404,7 +405,8 @@ xfs_dir2_leaf_readbuf(
* Read-ahead a contiguous directory block.
*/
if (i > mip->ra_current &&
map[mip->ra_index].br_blockcount >= geo->fsbcount) {
(map[mip->ra_index].br_blockcount - mip->ra_offset) >=
geo->fsbcount) {
xfs_dir3_data_readahead(dp,
map[mip->ra_index].br_startoff + mip->ra_offset,
XFS_FSB_TO_DADDR(dp->i_mount,
......@@ -425,14 +427,19 @@ xfs_dir2_leaf_readbuf(
}
/*
* Advance offset through the mapping table.
* Advance offset through the mapping table, processing a full
* dir block even if it is fragmented into several extents.
* But stop if we have consumed all valid mappings, even if
* it's not yet a full directory block.
*/
for (j = 0; j < geo->fsbcount; j += length ) {
for (j = 0;
j < geo->fsbcount && mip->ra_index < mip->map_valid;
j += length ) {
/*
* The rest of this extent but not more than a dir
* block.
*/
length = min_t(int, geo->fsbcount,
length = min_t(int, geo->fsbcount - j,
map[mip->ra_index].br_blockcount -
mip->ra_offset);
mip->ra_offset += length;
......
......@@ -132,6 +132,11 @@ xfs_trim_extents(
error = xfs_btree_decrement(cur, 0, &i);
if (error)
goto out_del_cursor;
if (fatal_signal_pending(current)) {
error = -ERESTARTSYS;
goto out_del_cursor;
}
}
out_del_cursor:
......@@ -196,8 +201,11 @@ xfs_ioc_trim(
for (agno = start_agno; agno <= end_agno; agno++) {
error = xfs_trim_extents(mp, agno, start, end, minlen,
&blocks_trimmed);
if (error)
if (error) {
last_error = error;
if (error == -ERESTARTSYS)
break;
}
}
if (last_error)
......
......@@ -290,6 +290,7 @@ void
xfs_efi_release(
struct xfs_efi_log_item *efip)
{
ASSERT(atomic_read(&efip->efi_refcount) > 0);
if (atomic_dec_and_test(&efip->efi_refcount)) {
xfs_trans_ail_remove(&efip->efi_item, SHUTDOWN_LOG_IO_ERROR);
xfs_efi_item_free(efip);
......
This diff is collapsed.
/*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __XFS_FSMAP_H__
#define __XFS_FSMAP_H__
struct fsmap;
/* internal fsmap representation */
struct xfs_fsmap {
dev_t fmr_device; /* device id */
uint32_t fmr_flags; /* mapping flags */
uint64_t fmr_physical; /* device offset of segment */
uint64_t fmr_owner; /* owner id */
xfs_fileoff_t fmr_offset; /* file offset of segment */
xfs_filblks_t fmr_length; /* length of segment, blocks */
};
struct xfs_fsmap_head {
uint32_t fmh_iflags; /* control flags */
uint32_t fmh_oflags; /* output flags */
unsigned int fmh_count; /* # of entries in array incl. input */
unsigned int fmh_entries; /* # of entries filled in (output). */
struct xfs_fsmap fmh_keys[2]; /* low and high keys */
};
void xfs_fsmap_from_internal(struct fsmap *dest, struct xfs_fsmap *src);
void xfs_fsmap_to_internal(struct xfs_fsmap *dest, struct fsmap *src);
/* fsmap to userspace formatter - copy to user & advance pointer */
typedef int (*xfs_fsmap_format_t)(struct xfs_fsmap *, void *);
int xfs_getfsmap(struct xfs_mount *mp, struct xfs_fsmap_head *head,
xfs_fsmap_format_t formatter, void *arg);
#endif /* __XFS_FSMAP_H__ */
......@@ -262,6 +262,22 @@ xfs_inode_clear_reclaim_tag(
xfs_perag_clear_reclaim_tag(pag);
}
static void
xfs_inew_wait(
struct xfs_inode *ip)
{
wait_queue_head_t *wq = bit_waitqueue(&ip->i_flags, __XFS_INEW_BIT);
DEFINE_WAIT_BIT(wait, &ip->i_flags, __XFS_INEW_BIT);
do {
prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE);
if (!xfs_iflags_test(ip, XFS_INEW))
break;
schedule();
} while (true);
finish_wait(wq, &wait.wait);
}
/*
* When we recycle a reclaimable inode, we need to re-initialise the VFS inode
* part of the structure. This is made more complex by the fact we store
......@@ -366,14 +382,17 @@ xfs_iget_cache_hit(
error = xfs_reinit_inode(mp, inode);
if (error) {
bool wake;
/*
* Re-initializing the inode failed, and we are in deep
* trouble. Try to re-add it to the reclaim list.
*/
rcu_read_lock();
spin_lock(&ip->i_flags_lock);
wake = !!__xfs_iflags_test(ip, XFS_INEW);
ip->i_flags &= ~(XFS_INEW | XFS_IRECLAIM);
if (wake)
wake_up_bit(&ip->i_flags, __XFS_INEW_BIT);
ASSERT(ip->i_flags & XFS_IRECLAIMABLE);
trace_xfs_iget_reclaim_fail(ip);
goto out_error;
......@@ -623,9 +642,11 @@ xfs_iget(
STATIC int
xfs_inode_ag_walk_grab(
struct xfs_inode *ip)
struct xfs_inode *ip,
int flags)
{
struct inode *inode = VFS_I(ip);
bool newinos = !!(flags & XFS_AGITER_INEW_WAIT);
ASSERT(rcu_read_lock_held());
......@@ -643,7 +664,8 @@ xfs_inode_ag_walk_grab(
goto out_unlock_noent;
/* avoid new or reclaimable inodes. Leave for reclaim code to flush */
if (__xfs_iflags_test(ip, XFS_INEW | XFS_IRECLAIMABLE | XFS_IRECLAIM))
if ((!newinos && __xfs_iflags_test(ip, XFS_INEW)) ||
__xfs_iflags_test(ip, XFS_IRECLAIMABLE | XFS_IRECLAIM))
goto out_unlock_noent;
spin_unlock(&ip->i_flags_lock);
......@@ -671,7 +693,8 @@ xfs_inode_ag_walk(
void *args),
int flags,
void *args,
int tag)
int tag,
int iter_flags)
{
uint32_t first_index;
int last_error = 0;
......@@ -713,7 +736,7 @@ xfs_inode_ag_walk(
for (i = 0; i < nr_found; i++) {
struct xfs_inode *ip = batch[i];
if (done || xfs_inode_ag_walk_grab(ip))
if (done || xfs_inode_ag_walk_grab(ip, iter_flags))
batch[i] = NULL;
/*
......@@ -741,6 +764,9 @@ xfs_inode_ag_walk(
for (i = 0; i < nr_found; i++) {
if (!batch[i])
continue;
if ((iter_flags & XFS_AGITER_INEW_WAIT) &&
xfs_iflags_test(batch[i], XFS_INEW))
xfs_inew_wait(batch[i]);
error = execute(batch[i], flags, args);
IRELE(batch[i]);
if (error == -EAGAIN) {
......@@ -820,12 +846,13 @@ xfs_cowblocks_worker(
}
int
xfs_inode_ag_iterator(
xfs_inode_ag_iterator_flags(
struct xfs_mount *mp,
int (*execute)(struct xfs_inode *ip, int flags,
void *args),
int flags,
void *args)
void *args,
int iter_flags)
{
struct xfs_perag *pag;
int error = 0;
......@@ -835,7 +862,8 @@ xfs_inode_ag_iterator(
ag = 0;
while ((pag = xfs_perag_get(mp, ag))) {
ag = pag->pag_agno + 1;
error = xfs_inode_ag_walk(mp, pag, execute, flags, args, -1);
error = xfs_inode_ag_walk(mp, pag, execute, flags, args, -1,
iter_flags);
xfs_perag_put(pag);
if (error) {
last_error = error;
......@@ -846,6 +874,17 @@ xfs_inode_ag_iterator(
return last_error;
}
int
xfs_inode_ag_iterator(
struct xfs_mount *mp,
int (*execute)(struct xfs_inode *ip, int flags,
void *args),
int flags,
void *args)
{
return xfs_inode_ag_iterator_flags(mp, execute, flags, args, 0);
}
int
xfs_inode_ag_iterator_tag(
struct xfs_mount *mp,
......@@ -863,7 +902,8 @@ xfs_inode_ag_iterator_tag(
ag = 0;
while ((pag = xfs_perag_get_tag(mp, ag, tag))) {
ag = pag->pag_agno + 1;
error = xfs_inode_ag_walk(mp, pag, execute, flags, args, tag);
error = xfs_inode_ag_walk(mp, pag, execute, flags, args, tag,
0);
xfs_perag_put(pag);
if (error) {
last_error = error;
......
......@@ -48,6 +48,11 @@ struct xfs_eofblocks {
#define XFS_IGET_UNTRUSTED 0x2
#define XFS_IGET_DONTCACHE 0x4
/*
* flags for AG inode iterator
*/
#define XFS_AGITER_INEW_WAIT 0x1 /* wait on new inodes */
int xfs_iget(struct xfs_mount *mp, struct xfs_trans *tp, xfs_ino_t ino,
uint flags, uint lock_flags, xfs_inode_t **ipp);
......@@ -79,6 +84,9 @@ void xfs_cowblocks_worker(struct work_struct *);
int xfs_inode_ag_iterator(struct xfs_mount *mp,
int (*execute)(struct xfs_inode *ip, int flags, void *args),
int flags, void *args);
int xfs_inode_ag_iterator_flags(struct xfs_mount *mp,
int (*execute)(struct xfs_inode *ip, int flags, void *args),
int flags, void *args, int iter_flags);
int xfs_inode_ag_iterator_tag(struct xfs_mount *mp,
int (*execute)(struct xfs_inode *ip, int flags, void *args),
int flags, void *args, int tag);
......
......@@ -1906,12 +1906,13 @@ xfs_inactive(
* force is true because we are evicting an inode from the
* cache. Post-eof blocks must be freed, lest we end up with
* broken free space accounting.
*
* Note: don't bother with iolock here since lockdep complains
* about acquiring it in reclaim context. We have the only
* reference to the inode at this point anyways.
*/
if (xfs_can_free_eofblocks(ip, true)) {
xfs_ilock(ip, XFS_IOLOCK_EXCL);
if (xfs_can_free_eofblocks(ip, true))
xfs_free_eofblocks(ip);
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
}
return;
}
......
......@@ -216,7 +216,8 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
#define XFS_IRECLAIM (1 << 0) /* started reclaiming this inode */
#define XFS_ISTALE (1 << 1) /* inode has been staled */
#define XFS_IRECLAIMABLE (1 << 2) /* inode can be reclaimed */
#define XFS_INEW (1 << 3) /* inode has just been allocated */
#define __XFS_INEW_BIT 3 /* inode has just been allocated */
#define XFS_INEW (1 << __XFS_INEW_BIT)
#define XFS_ITRUNCATED (1 << 5) /* truncated down so flush-on-close */
#define XFS_IDIRTY_RELEASE (1 << 6) /* dirty release already seen */
#define __XFS_IFLOCK_BIT 7 /* inode is being flushed right now */
......@@ -464,6 +465,7 @@ static inline void xfs_finish_inode_setup(struct xfs_inode *ip)
xfs_iflags_clear(ip, XFS_INEW);
barrier();
unlock_new_inode(VFS_I(ip));
wake_up_bit(&ip->i_flags, __XFS_INEW_BIT);
}
static inline void xfs_setup_existing_inode(struct xfs_inode *ip)
......
......@@ -731,22 +731,27 @@ xfs_iflush_done(
* holding the lock before removing the inode from the AIL.
*/
if (need_ail) {
struct xfs_log_item *log_items[need_ail];
int i = 0;
bool mlip_changed = false;
/* this is an opencoded batch version of xfs_trans_ail_delete */
spin_lock(&ailp->xa_lock);
for (blip = lip; blip; blip = blip->li_bio_list) {
iip = INODE_ITEM(blip);
if (iip->ili_logged &&
blip->li_lsn == iip->ili_flush_lsn) {
log_items[i++] = blip;
}
ASSERT(i <= need_ail);
if (INODE_ITEM(blip)->ili_logged &&
blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn)
mlip_changed |= xfs_ail_delete_one(ailp, blip);
}
/* xfs_trans_ail_delete_bulk() drops the AIL lock. */
xfs_trans_ail_delete_bulk(ailp, log_items, i,
SHUTDOWN_CORRUPT_INCORE);
}
if (mlip_changed) {
if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
xlog_assign_tail_lsn_locked(ailp->xa_mount);
if (list_empty(&ailp->xa_ail))
wake_up_all(&ailp->xa_empty);
}
spin_unlock(&ailp->xa_lock);
if (mlip_changed)
xfs_log_space_wake(ailp->xa_mount);
}
/*
* clean up and unlock the flush lock now we are done. We can clear the
......
......@@ -41,6 +41,9 @@
#include "xfs_trans.h"
#include "xfs_pnfs.h"
#include "xfs_acl.h"
#include "xfs_btree.h"
#include <linux/fsmap.h>
#include "xfs_fsmap.h"
#include <linux/capability.h>
#include <linux/cred.h>
......@@ -1543,10 +1546,11 @@ xfs_ioc_getbmap(
unsigned int cmd,
void __user *arg)
{
struct getbmapx bmx;
struct getbmapx bmx = { 0 };
int error;
if (copy_from_user(&bmx, arg, sizeof(struct getbmapx)))
/* struct getbmap is a strict subset of struct getbmapx. */
if (copy_from_user(&bmx, arg, offsetof(struct getbmapx, bmv_iflags)))
return -EFAULT;
if (bmx.bmv_count < 2)
......@@ -1608,6 +1612,84 @@ xfs_ioc_getbmapx(
return 0;
}
struct getfsmap_info {
struct xfs_mount *mp;
struct fsmap_head __user *data;
unsigned int idx;
__u32 last_flags;
};
STATIC int
xfs_getfsmap_format(struct xfs_fsmap *xfm, void *priv)
{
struct getfsmap_info *info = priv;
struct fsmap fm;
trace_xfs_getfsmap_mapping(info->mp, xfm);
info->last_flags = xfm->fmr_flags;
xfs_fsmap_from_internal(&fm, xfm);
if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
sizeof(struct fsmap)))
return -EFAULT;
return 0;
}
STATIC int
xfs_ioc_getfsmap(
struct xfs_inode *ip,
struct fsmap_head __user *arg)
{
struct getfsmap_info info = { NULL };
struct xfs_fsmap_head xhead = {0};
struct fsmap_head head;
bool aborted = false;
int error;
if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
return -EFAULT;
if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
sizeof(head.fmh_keys[0].fmr_reserved)) ||
memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
sizeof(head.fmh_keys[1].fmr_reserved)))
return -EINVAL;
xhead.fmh_iflags = head.fmh_iflags;
xhead.fmh_count = head.fmh_count;
xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]);
xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]);
trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]);
info.mp = ip->i_mount;
info.data = arg;
error = xfs_getfsmap(ip->i_mount, &xhead, xfs_getfsmap_format, &info);
if (error == XFS_BTREE_QUERY_RANGE_ABORT) {
error = 0;
aborted = true;
} else if (error)
return error;
/* If we didn't abort, set the "last" flag in the last fmx */
if (!aborted && info.idx) {
info.last_flags |= FMR_OF_LAST;
if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
&info.last_flags, sizeof(info.last_flags)))
return -EFAULT;
}
/* copy back header */
head.fmh_entries = xhead.fmh_entries;
head.fmh_oflags = xhead.fmh_oflags;
if (copy_to_user(arg, &head, sizeof(struct fsmap_head)))
return -EFAULT;
return 0;
}
int
xfs_ioc_swapext(
xfs_swapext_t *sxp)
......@@ -1788,6 +1870,9 @@ xfs_file_ioctl(
case XFS_IOC_GETBMAPX:
return xfs_ioc_getbmapx(ip, arg);
case FS_IOC_GETFSMAP:
return xfs_ioc_getfsmap(ip, arg);
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE:
case XFS_IOC_PATH_TO_FSHANDLE: {
......
......@@ -20,6 +20,7 @@
#include <linux/mount.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fsmap.h>
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_format.h"
......@@ -554,6 +555,7 @@ xfs_file_compat_ioctl(
case XFS_IOC_GOINGDOWN:
case XFS_IOC_ERROR_INJECTION:
case XFS_IOC_ERROR_CLEARALL:
case FS_IOC_GETFSMAP:
return xfs_file_ioctl(filp, cmd, p);
#ifndef BROKEN_X86_ALIGNMENT
/* These are handled fine if no alignment issues */
......
......@@ -240,7 +240,7 @@ xfs_iomap_write_direct(
*/
if (IS_DAX(VFS_I(ip))) {
bmapi_flags = XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO;
if (ISUNWRITTEN(imap)) {
if (imap->br_state == XFS_EXT_UNWRITTEN) {
tflags |= XFS_TRANS_RESERVE;
resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0) << 1;
}
......@@ -945,7 +945,7 @@ static inline bool imap_needs_alloc(struct inode *inode,
return !nimaps ||
imap->br_startblock == HOLESTARTBLOCK ||
imap->br_startblock == DELAYSTARTBLOCK ||
(IS_DAX(inode) && ISUNWRITTEN(imap));
(IS_DAX(inode) && imap->br_state == XFS_EXT_UNWRITTEN);
}
static inline bool need_excl_ilock(struct xfs_inode *ip, unsigned flags)
......@@ -1180,10 +1180,10 @@ xfs_xattr_iomap_begin(
if (XFS_FORCED_SHUTDOWN(mp))
return -EIO;
lockmode = xfs_ilock_data_map_shared(ip);
lockmode = xfs_ilock_attr_map_shared(ip);
/* if there are no attribute fork or extents, return ENOENT */
if (XFS_IFORK_Q(ip) || !ip->i_d.di_anextents) {
if (!XFS_IFORK_Q(ip) || !ip->i_d.di_anextents) {
error = -ENOENT;
goto out_unlock;
}
......
......@@ -212,88 +212,6 @@ static inline kgid_t xfs_gid_to_kgid(__uint32_t gid)
#define xfs_sort(a,n,s,fn) sort(a,n,s,fn,NULL)
#define xfs_stack_trace() dump_stack()
/* Move the kernel do_div definition off to one side */
#if defined __i386__
/* For ia32 we need to pull some tricks to get past various versions
* of the compiler which do not like us using do_div in the middle
* of large functions.
*/
static inline __u32 xfs_do_div(void *a, __u32 b, int n)
{
__u32 mod;
switch (n) {
case 4:
mod = *(__u32 *)a % b;
*(__u32 *)a = *(__u32 *)a / b;
return mod;
case 8:
{
unsigned long __upper, __low, __high, __mod;
__u64 c = *(__u64 *)a;
__upper = __high = c >> 32;
__low = c;
if (__high) {
__upper = __high % (b);
__high = __high / (b);
}
asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
asm("":"=A" (c):"a" (__low),"d" (__high));
*(__u64 *)a = c;
return __mod;
}
}
/* NOTREACHED */
return 0;
}
/* Side effect free 64 bit mod operation */
static inline __u32 xfs_do_mod(void *a, __u32 b, int n)
{
switch (n) {
case 4:
return *(__u32 *)a % b;
case 8:
{
unsigned long __upper, __low, __high, __mod;
__u64 c = *(__u64 *)a;
__upper = __high = c >> 32;
__low = c;
if (__high) {
__upper = __high % (b);
__high = __high / (b);
}
asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
asm("":"=A" (c):"a" (__low),"d" (__high));
return __mod;
}
}
/* NOTREACHED */
return 0;
}
#else
static inline __u32 xfs_do_div(void *a, __u32 b, int n)
{
__u32 mod;
switch (n) {
case 4:
mod = *(__u32 *)a % b;
*(__u32 *)a = *(__u32 *)a / b;
return mod;
case 8:
mod = do_div(*(__u64 *)a, b);
return mod;
}
/* NOTREACHED */
return 0;
}
/* Side effect free 64 bit mod operation */
static inline __u32 xfs_do_mod(void *a, __u32 b, int n)
{
......@@ -310,10 +228,7 @@ static inline __u32 xfs_do_mod(void *a, __u32 b, int n)
/* NOTREACHED */
return 0;
}
#endif
#undef do_div
#define do_div(a, b) xfs_do_div(&(a), (b), sizeof(a))
#define do_mod(a, b) xfs_do_mod(&(a), (b), sizeof(a))
static inline __uint64_t roundup_64(__uint64_t x, __uint32_t y)
......
......@@ -1293,7 +1293,7 @@ void
xfs_log_work_queue(
struct xfs_mount *mp)
{
queue_delayed_work(mp->m_log_workqueue, &mp->m_log->l_work,
queue_delayed_work(mp->m_sync_workqueue, &mp->m_log->l_work,
msecs_to_jiffies(xfs_syncd_centisecs * 10));
}
......@@ -1852,7 +1852,7 @@ xlog_sync(
*/
if (log->l_badcrc_factor &&
(prandom_u32() % log->l_badcrc_factor == 0)) {
iclog->ic_header.h_crc &= 0xAAAAAAAA;
iclog->ic_header.h_crc &= cpu_to_le32(0xAAAAAAAA);
iclog->ic_state |= XLOG_STATE_IOABORT;
xfs_warn(log->l_mp,
"Intentionally corrupted log record at LSN 0x%llx. Shutdown imminent.",
......
......@@ -73,6 +73,10 @@ xfs_uuid_mount(
uuid_t *uuid = &mp->m_sb.sb_uuid;
int hole, i;
/* Publish UUID in struct super_block */
BUILD_BUG_ON(sizeof(mp->m_super->s_uuid) != sizeof(uuid_t));
memcpy(&mp->m_super->s_uuid, uuid, sizeof(uuid_t));
if (mp->m_flags & XFS_MOUNT_NOUUID)
return 0;
......
......@@ -183,6 +183,7 @@ typedef struct xfs_mount {
struct workqueue_struct *m_reclaim_workqueue;
struct workqueue_struct *m_log_workqueue;
struct workqueue_struct *m_eofblocks_workqueue;
struct workqueue_struct *m_sync_workqueue;
/*
* Generation of the filesysyem layout. This is incremented by each
......@@ -312,7 +313,7 @@ void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
static inline xfs_agnumber_t
xfs_daddr_to_agno(struct xfs_mount *mp, xfs_daddr_t d)
{
xfs_daddr_t ld = XFS_BB_TO_FSBT(mp, d);
xfs_rfsblock_t ld = XFS_BB_TO_FSBT(mp, d);
do_div(ld, mp->m_sb.sb_agblocks);
return (xfs_agnumber_t) ld;
}
......@@ -320,7 +321,7 @@ xfs_daddr_to_agno(struct xfs_mount *mp, xfs_daddr_t d)
static inline xfs_agblock_t
xfs_daddr_to_agbno(struct xfs_mount *mp, xfs_daddr_t d)
{
xfs_daddr_t ld = XFS_BB_TO_FSBT(mp, d);
xfs_rfsblock_t ld = XFS_BB_TO_FSBT(mp, d);
return (xfs_agblock_t) do_div(ld, mp->m_sb.sb_agblocks);
}
......
......@@ -851,8 +851,8 @@ xfs_qm_reset_dqcounts(
* started afresh by xfs_qm_quotacheck.
*/
#ifdef DEBUG
j = XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
do_div(j, sizeof(xfs_dqblk_t));
j = (int)XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB) /
sizeof(xfs_dqblk_t);
ASSERT(mp->m_quotainfo->qi_dqperchunk == j);
#endif
dqb = bp->b_addr;
......@@ -1384,12 +1384,7 @@ xfs_qm_quotacheck(
mp->m_qflags |= flags;
error_return:
while (!list_empty(&buffer_list)) {
struct xfs_buf *bp =
list_first_entry(&buffer_list, struct xfs_buf, b_list);
list_del_init(&bp->b_list);
xfs_buf_relse(bp);
}
xfs_buf_delwri_cancel(&buffer_list);
if (error) {
xfs_warn(mp,
......
......@@ -759,5 +759,6 @@ xfs_qm_dqrele_all_inodes(
uint flags)
{
ASSERT(mp->m_quotainfo);
xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags, NULL);
xfs_inode_ag_iterator_flags(mp, xfs_dqrele_inode, flags, NULL,
XFS_AGITER_INEW_WAIT);
}
......@@ -221,6 +221,7 @@ void
xfs_cui_release(
struct xfs_cui_log_item *cuip)
{
ASSERT(atomic_read(&cuip->cui_refcount) > 0);
if (atomic_dec_and_test(&cuip->cui_refcount)) {
xfs_trans_ail_remove(&cuip->cui_item, SHUTDOWN_LOG_IO_ERROR);
xfs_cui_item_free(cuip);
......
......@@ -206,11 +206,7 @@ xfs_reflink_trim_around_shared(
int error = 0;
/* Holes, unwritten, and delalloc extents cannot be shared */
if (!xfs_is_reflink_inode(ip) ||
ISUNWRITTEN(irec) ||
irec->br_startblock == HOLESTARTBLOCK ||
irec->br_startblock == DELAYSTARTBLOCK ||
isnullstartblock(irec->br_startblock)) {
if (!xfs_is_reflink_inode(ip) || !xfs_bmap_is_real_extent(irec)) {
*shared = false;
return 0;
}
......@@ -709,8 +705,22 @@ xfs_reflink_end_cow(
offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
/* Start a rolling transaction to switch the mappings */
resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
/*
* Start a rolling transaction to switch the mappings. We're
* unlikely ever to have to remap 16T worth of single-block
* extents, so just cap the worst case extent count to 2^32-1.
* Stick a warning in just in case, and avoid 64-bit division.
*/
BUILD_BUG_ON(MAX_RW_COUNT > UINT_MAX);
if (end_fsb - offset_fsb > UINT_MAX) {
error = -EFSCORRUPTED;
xfs_force_shutdown(ip->i_mount, SHUTDOWN_CORRUPT_INCORE);
ASSERT(0);
goto out;
}
resblks = XFS_NEXTENTADD_SPACE_RES(ip->i_mount,
(unsigned int)(end_fsb - offset_fsb),
XFS_DATA_FORK);
error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
resblks, 0, 0, &tp);
if (error)
......@@ -1045,12 +1055,12 @@ xfs_reflink_remap_extent(
xfs_off_t new_isize)
{
struct xfs_mount *mp = ip->i_mount;
bool real_extent = xfs_bmap_is_real_extent(irec);
struct xfs_trans *tp;
xfs_fsblock_t firstfsb;
unsigned int resblks;
struct xfs_defer_ops dfops;
struct xfs_bmbt_irec uirec;
bool real_extent;
xfs_filblks_t rlen;
xfs_filblks_t unmap_len;
xfs_off_t newlen;
......@@ -1059,11 +1069,6 @@ xfs_reflink_remap_extent(
unmap_len = irec->br_startoff + irec->br_blockcount - destoff;
trace_xfs_reflink_punch_range(ip, destoff, unmap_len);
/* Only remap normal extents. */
real_extent = (irec->br_startblock != HOLESTARTBLOCK &&
irec->br_startblock != DELAYSTARTBLOCK &&
!ISUNWRITTEN(irec));
/* No reflinking if we're low on space */
if (real_extent) {
error = xfs_reflink_ag_has_free_space(mp,
......@@ -1359,9 +1364,7 @@ xfs_reflink_dirty_extents(
goto out;
if (nmaps == 0)
break;
if (map[0].br_startblock == HOLESTARTBLOCK ||
map[0].br_startblock == DELAYSTARTBLOCK ||
ISUNWRITTEN(&map[0]))
if (!xfs_bmap_is_real_extent(&map[0]))
goto next;
map[1] = map[0];
......@@ -1435,9 +1438,7 @@ xfs_reflink_clear_inode_flag(
return error;
if (nmaps == 0)
break;
if (map.br_startblock == HOLESTARTBLOCK ||
map.br_startblock == DELAYSTARTBLOCK ||
ISUNWRITTEN(&map))
if (!xfs_bmap_is_real_extent(&map))
goto next;
agno = XFS_FSB_TO_AGNO(mp, map.br_startblock);
......
......@@ -243,6 +243,7 @@ void
xfs_rui_release(
struct xfs_rui_log_item *ruip)
{
ASSERT(atomic_read(&ruip->rui_refcount) > 0);
if (atomic_dec_and_test(&ruip->rui_refcount)) {
xfs_trans_ail_remove(&ruip->rui_item, SHUTDOWN_LOG_IO_ERROR);
xfs_rui_item_free(ruip);
......
......@@ -23,6 +23,16 @@
struct xfs_mount;
struct xfs_trans;
struct xfs_rtalloc_rec {
xfs_rtblock_t ar_startblock;
xfs_rtblock_t ar_blockcount;
};
typedef int (*xfs_rtalloc_query_range_fn)(
struct xfs_trans *tp,
struct xfs_rtalloc_rec *rec,
void *priv);
#ifdef CONFIG_XFS_RT
/*
* Function prototypes for exported functions.
......@@ -118,13 +128,21 @@ int xfs_rtmodify_summary(struct xfs_mount *mp, struct xfs_trans *tp, int log,
int xfs_rtfree_range(struct xfs_mount *mp, struct xfs_trans *tp,
xfs_rtblock_t start, xfs_extlen_t len,
struct xfs_buf **rbpp, xfs_fsblock_t *rsb);
int xfs_rtalloc_query_range(struct xfs_trans *tp,
struct xfs_rtalloc_rec *low_rec,
struct xfs_rtalloc_rec *high_rec,
xfs_rtalloc_query_range_fn fn,
void *priv);
int xfs_rtalloc_query_all(struct xfs_trans *tp,
xfs_rtalloc_query_range_fn fn,
void *priv);
#else
# define xfs_rtallocate_extent(t,b,min,max,l,f,p,rb) (ENOSYS)
# define xfs_rtfree_extent(t,b,l) (ENOSYS)
# define xfs_rtpick_extent(m,t,l,rb) (ENOSYS)
# define xfs_growfs_rt(mp,in) (ENOSYS)
# define xfs_rtalloc_query_range(t,l,h,f,p) (ENOSYS)
# define xfs_rtalloc_query_all(t,f,p) (ENOSYS)
static inline int /* error */
xfs_rtmount_init(
xfs_mount_t *mp) /* file system mount structure */
......
......@@ -877,8 +877,15 @@ xfs_init_mount_workqueues(
if (!mp->m_eofblocks_workqueue)
goto out_destroy_log;
mp->m_sync_workqueue = alloc_workqueue("xfs-sync/%s", WQ_FREEZABLE, 0,
mp->m_fsname);
if (!mp->m_sync_workqueue)
goto out_destroy_eofb;
return 0;
out_destroy_eofb:
destroy_workqueue(mp->m_eofblocks_workqueue);
out_destroy_log:
destroy_workqueue(mp->m_log_workqueue);
out_destroy_reclaim:
......@@ -899,6 +906,7 @@ STATIC void
xfs_destroy_mount_workqueues(
struct xfs_mount *mp)
{
destroy_workqueue(mp->m_sync_workqueue);
destroy_workqueue(mp->m_eofblocks_workqueue);
destroy_workqueue(mp->m_log_workqueue);
destroy_workqueue(mp->m_reclaim_workqueue);
......
......@@ -47,6 +47,7 @@
#include "xfs_inode_item.h"
#include "xfs_bmap_btree.h"
#include "xfs_filestream.h"
#include "xfs_fsmap.h"
/*
* We include this last to have the helpers above available for the trace
......
......@@ -40,6 +40,8 @@ struct xfs_inode_log_format;
struct xfs_bmbt_irec;
struct xfs_btree_cur;
struct xfs_refcount_irec;
struct xfs_fsmap;
struct xfs_rmap_irec;
DECLARE_EVENT_CLASS(xfs_attr_list_class,
TP_PROTO(struct xfs_attr_list_context *ctx),
......@@ -2190,7 +2192,7 @@ DECLARE_EVENT_CLASS(xfs_discard_class,
__entry->agbno = agbno;
__entry->len = len;
),
TP_printk("dev %d:%d agno %u agbno %u len %u\n",
TP_printk("dev %d:%d agno %u agbno %u len %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->agbno,
......@@ -2253,8 +2255,8 @@ DECLARE_EVENT_CLASS(xfs_defer_class,
TP_STRUCT__entry(
__field(dev_t, dev)
__field(void *, dop)
__field(bool, committed)
__field(bool, low)
__field(char, committed)
__field(char, low)
),
TP_fast_assign(
__entry->dev = mp ? mp->m_super->s_dev : 0;
......@@ -2262,7 +2264,7 @@ DECLARE_EVENT_CLASS(xfs_defer_class,
__entry->committed = dop->dop_committed;
__entry->low = dop->dop_low;
),
TP_printk("dev %d:%d ops %p committed %d low %d\n",
TP_printk("dev %d:%d ops %p committed %d low %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->dop,
__entry->committed,
......@@ -2279,8 +2281,8 @@ DECLARE_EVENT_CLASS(xfs_defer_error_class,
TP_STRUCT__entry(
__field(dev_t, dev)
__field(void *, dop)
__field(bool, committed)
__field(bool, low)
__field(char, committed)
__field(char, low)
__field(int, error)
),
TP_fast_assign(
......@@ -2290,7 +2292,7 @@ DECLARE_EVENT_CLASS(xfs_defer_error_class,
__entry->low = dop->dop_low;
__entry->error = error;
),
TP_printk("dev %d:%d ops %p committed %d low %d err %d\n",
TP_printk("dev %d:%d ops %p committed %d low %d err %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->dop,
__entry->committed,
......@@ -2309,7 +2311,7 @@ DECLARE_EVENT_CLASS(xfs_defer_pending_class,
__field(dev_t, dev)
__field(int, type)
__field(void *, intent)
__field(bool, committed)
__field(char, committed)
__field(int, nr)
),
TP_fast_assign(
......@@ -2319,7 +2321,7 @@ DECLARE_EVENT_CLASS(xfs_defer_pending_class,
__entry->committed = dfp->dfp_done != NULL;
__entry->nr = dfp->dfp_count;
),
TP_printk("dev %d:%d optype %d intent %p committed %d nr %d\n",
TP_printk("dev %d:%d optype %d intent %p committed %d nr %d",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->type,
__entry->intent,
......@@ -2614,7 +2616,8 @@ DECLARE_EVENT_CLASS(xfs_ag_resv_class,
__entry->asked = r ? r->ar_asked : 0;
__entry->len = len;
),
TP_printk("dev %d:%d agno %u resv %d freeblks %u flcount %u resv %u ask %u len %u\n",
TP_printk("dev %d:%d agno %u resv %d freeblks %u flcount %u "
"resv %u ask %u len %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->resv,
......@@ -2667,7 +2670,7 @@ DECLARE_EVENT_CLASS(xfs_ag_btree_lookup_class,
__entry->agbno = agbno;
__entry->dir = dir;
),
TP_printk("dev %d:%d agno %u agbno %u cmp %s(%d)\n",
TP_printk("dev %d:%d agno %u agbno %u cmp %s(%d)",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->agbno,
......@@ -2700,7 +2703,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_class,
__entry->blockcount = irec->rc_blockcount;
__entry->refcount = irec->rc_refcount;
),
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u\n",
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->startblock,
......@@ -2735,7 +2738,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class,
__entry->refcount = irec->rc_refcount;
__entry->agbno = agbno;
),
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u @ agbno %u\n",
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u @ agbno %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->startblock,
......@@ -2776,7 +2779,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class,
__entry->i2_refcount = i2->rc_refcount;
),
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
"agbno %u len %u refcount %u\n",
"agbno %u len %u refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->i1_startblock,
......@@ -2822,7 +2825,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class,
__entry->agbno = agbno;
),
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
"agbno %u len %u refcount %u @ agbno %u\n",
"agbno %u len %u refcount %u @ agbno %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->i1_startblock,
......@@ -2875,7 +2878,7 @@ DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class,
),
TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
"agbno %u len %u refcount %u -- "
"agbno %u len %u refcount %u\n",
"agbno %u len %u refcount %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->i1_startblock,
......@@ -3001,31 +3004,6 @@ DEFINE_EVENT(xfs_inode_error_class, name, \
unsigned long caller_ip), \
TP_ARGS(ip, error, caller_ip))
/* reflink allocator */
TRACE_EVENT(xfs_bmap_remap_alloc,
TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t fsbno,
xfs_extlen_t len),
TP_ARGS(ip, fsbno, len),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsblock_t, fsbno)
__field(xfs_extlen_t, len)
),
TP_fast_assign(
__entry->dev = VFS_I(ip)->i_sb->s_dev;
__entry->ino = ip->i_ino;
__entry->fsbno = fsbno;
__entry->len = len;
),
TP_printk("dev %d:%d ino 0x%llx fsbno 0x%llx len %x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->fsbno,
__entry->len)
);
DEFINE_INODE_ERROR_EVENT(xfs_bmap_remap_alloc_error);
/* reflink tracepoint classes */
/* two-file io tracepoint class */
......@@ -3227,7 +3205,7 @@ TRACE_EVENT(xfs_ioctl_clone,
),
TP_printk("dev %d:%d "
"ino 0x%lx isize 0x%llx -> "
"ino 0x%lx isize 0x%llx\n",
"ino 0x%lx isize 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->src_ino,
__entry->src_isize,
......@@ -3267,6 +3245,88 @@ DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap);
DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap_piece);
DEFINE_INODE_ERROR_EVENT(xfs_swap_extent_rmap_error);
/* fsmap traces */
DECLARE_EVENT_CLASS(xfs_fsmap_class,
TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_agnumber_t agno,
struct xfs_rmap_irec *rmap),
TP_ARGS(mp, keydev, agno, rmap),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(dev_t, keydev)
__field(xfs_agnumber_t, agno)
__field(xfs_fsblock_t, bno)
__field(xfs_filblks_t, len)
__field(__uint64_t, owner)
__field(__uint64_t, offset)
__field(unsigned int, flags)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->keydev = new_decode_dev(keydev);
__entry->agno = agno;
__entry->bno = rmap->rm_startblock;
__entry->len = rmap->rm_blockcount;
__entry->owner = rmap->rm_owner;
__entry->offset = rmap->rm_offset;
__entry->flags = rmap->rm_flags;
),
TP_printk("dev %d:%d keydev %d:%d agno %u bno %llu len %llu owner %lld offset %llu flags 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
MAJOR(__entry->keydev), MINOR(__entry->keydev),
__entry->agno,
__entry->bno,
__entry->len,
__entry->owner,
__entry->offset,
__entry->flags)
)
#define DEFINE_FSMAP_EVENT(name) \
DEFINE_EVENT(xfs_fsmap_class, name, \
TP_PROTO(struct xfs_mount *mp, u32 keydev, xfs_agnumber_t agno, \
struct xfs_rmap_irec *rmap), \
TP_ARGS(mp, keydev, agno, rmap))
DEFINE_FSMAP_EVENT(xfs_fsmap_low_key);
DEFINE_FSMAP_EVENT(xfs_fsmap_high_key);
DEFINE_FSMAP_EVENT(xfs_fsmap_mapping);
DECLARE_EVENT_CLASS(xfs_getfsmap_class,
TP_PROTO(struct xfs_mount *mp, struct xfs_fsmap *fsmap),
TP_ARGS(mp, fsmap),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(dev_t, keydev)
__field(xfs_daddr_t, block)
__field(xfs_daddr_t, len)
__field(__uint64_t, owner)
__field(__uint64_t, offset)
__field(__uint64_t, flags)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->keydev = new_decode_dev(fsmap->fmr_device);
__entry->block = fsmap->fmr_physical;
__entry->len = fsmap->fmr_length;
__entry->owner = fsmap->fmr_owner;
__entry->offset = fsmap->fmr_offset;
__entry->flags = fsmap->fmr_flags;
),
TP_printk("dev %d:%d keydev %d:%d block %llu len %llu owner %lld offset %llu flags 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
MAJOR(__entry->keydev), MINOR(__entry->keydev),
__entry->block,
__entry->len,
__entry->owner,
__entry->offset,
__entry->flags)
)
#define DEFINE_GETFSMAP_EVENT(name) \
DEFINE_EVENT(xfs_getfsmap_class, name, \
TP_PROTO(struct xfs_mount *mp, struct xfs_fsmap *fsmap), \
TP_ARGS(mp, fsmap))
DEFINE_GETFSMAP_EVENT(xfs_getfsmap_low_key);
DEFINE_GETFSMAP_EVENT(xfs_getfsmap_high_key);
DEFINE_GETFSMAP_EVENT(xfs_getfsmap_mapping);
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
......
......@@ -262,6 +262,28 @@ xfs_trans_alloc(
return 0;
}
/*
* Create an empty transaction with no reservation. This is a defensive
* mechanism for routines that query metadata without actually modifying
* them -- if the metadata being queried is somehow cross-linked (think a
* btree block pointer that points higher in the tree), we risk deadlock.
* However, blocks grabbed as part of a transaction can be re-grabbed.
* The verifiers will notice the corrupt block and the operation will fail
* back to userspace without deadlocking.
*
* Note the zero-length reservation; this transaction MUST be cancelled
* without any dirty data.
*/
int
xfs_trans_alloc_empty(
struct xfs_mount *mp,
struct xfs_trans **tpp)
{
struct xfs_trans_res resv = {0};
return xfs_trans_alloc(mp, &resv, 0, 0, XFS_TRANS_NO_WRITECOUNT, tpp);
}
/*
* Record the indicated change to the given field for application
* to the file system's superblock when the transaction commits.
......@@ -1012,17 +1034,14 @@ xfs_trans_cancel(
* chunk we've been working on and get a new transaction to continue.
*/
int
__xfs_trans_roll(
xfs_trans_roll(
struct xfs_trans **tpp,
struct xfs_inode *dp,
int *committed)
struct xfs_inode *dp)
{
struct xfs_trans *trans;
struct xfs_trans_res tres;
int error;
*committed = 0;
/*
* Ensure that the inode is always logged.
*/
......@@ -1048,7 +1067,6 @@ __xfs_trans_roll(
if (error)
return error;
*committed = 1;
trans = *tpp;
/*
......@@ -1071,12 +1089,3 @@ __xfs_trans_roll(
xfs_trans_ijoin(trans, dp, 0);
return 0;
}
int
xfs_trans_roll(
struct xfs_trans **tpp,
struct xfs_inode *dp)
{
int committed;
return __xfs_trans_roll(tpp, dp, &committed);
}
......@@ -158,6 +158,8 @@ typedef struct xfs_trans {
int xfs_trans_alloc(struct xfs_mount *mp, struct xfs_trans_res *resp,
uint blocks, uint rtextents, uint flags,
struct xfs_trans **tpp);
int xfs_trans_alloc_empty(struct xfs_mount *mp,
struct xfs_trans **tpp);
void xfs_trans_mod_sb(xfs_trans_t *, uint, int64_t);
struct xfs_buf *xfs_trans_get_buf_map(struct xfs_trans *tp,
......@@ -226,7 +228,6 @@ int xfs_trans_free_extent(struct xfs_trans *,
struct xfs_efd_log_item *, xfs_fsblock_t,
xfs_extlen_t, struct xfs_owner_info *);
int xfs_trans_commit(struct xfs_trans *);
int __xfs_trans_roll(struct xfs_trans **, struct xfs_inode *, int *);
int xfs_trans_roll(struct xfs_trans **, struct xfs_inode *);
void xfs_trans_cancel(xfs_trans_t *);
int xfs_trans_ail_init(struct xfs_mount *);
......
......@@ -684,8 +684,23 @@ xfs_trans_ail_update_bulk(
}
}
/*
* xfs_trans_ail_delete_bulk - remove multiple log items from the AIL
bool
xfs_ail_delete_one(
struct xfs_ail *ailp,
struct xfs_log_item *lip)
{
struct xfs_log_item *mlip = xfs_ail_min(ailp);
trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
xfs_ail_delete(ailp, lip);
lip->li_flags &= ~XFS_LI_IN_AIL;
lip->li_lsn = 0;
return mlip == lip;
}
/**
* Remove a log items from the AIL
*
* @xfs_trans_ail_delete_bulk takes an array of log items that all need to
* removed from the AIL. The caller is already holding the AIL lock, and done
......@@ -706,52 +721,36 @@ xfs_trans_ail_update_bulk(
* before returning.
*/
void
xfs_trans_ail_delete_bulk(
xfs_trans_ail_delete(
struct xfs_ail *ailp,
struct xfs_log_item **log_items,
int nr_items,
struct xfs_log_item *lip,
int shutdown_type) __releases(ailp->xa_lock)
{
xfs_log_item_t *mlip;
int mlip_changed = 0;
int i;
struct xfs_mount *mp = ailp->xa_mount;
bool mlip_changed;
mlip = xfs_ail_min(ailp);
for (i = 0; i < nr_items; i++) {
struct xfs_log_item *lip = log_items[i];
if (!(lip->li_flags & XFS_LI_IN_AIL)) {
struct xfs_mount *mp = ailp->xa_mount;
spin_unlock(&ailp->xa_lock);
if (!XFS_FORCED_SHUTDOWN(mp)) {
xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
"%s: attempting to delete a log item that is not in the AIL",
__func__);
xfs_force_shutdown(mp, shutdown_type);
}
return;
if (!(lip->li_flags & XFS_LI_IN_AIL)) {
spin_unlock(&ailp->xa_lock);
if (!XFS_FORCED_SHUTDOWN(mp)) {
xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
"%s: attempting to delete a log item that is not in the AIL",
__func__);
xfs_force_shutdown(mp, shutdown_type);
}
trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
xfs_ail_delete(ailp, lip);
lip->li_flags &= ~XFS_LI_IN_AIL;
lip->li_lsn = 0;
if (mlip == lip)
mlip_changed = 1;
return;
}
mlip_changed = xfs_ail_delete_one(ailp, lip);
if (mlip_changed) {
if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
xlog_assign_tail_lsn_locked(ailp->xa_mount);
if (!XFS_FORCED_SHUTDOWN(mp))
xlog_assign_tail_lsn_locked(mp);
if (list_empty(&ailp->xa_ail))
wake_up_all(&ailp->xa_empty);
spin_unlock(&ailp->xa_lock);
}
spin_unlock(&ailp->xa_lock);
if (mlip_changed)
xfs_log_space_wake(ailp->xa_mount);
} else {
spin_unlock(&ailp->xa_lock);
}
}
int
......
......@@ -106,18 +106,9 @@ xfs_trans_ail_update(
xfs_trans_ail_update_bulk(ailp, NULL, &lip, 1, lsn);
}
void xfs_trans_ail_delete_bulk(struct xfs_ail *ailp,
struct xfs_log_item **log_items, int nr_items,
int shutdown_type)
__releases(ailp->xa_lock);
static inline void
xfs_trans_ail_delete(
struct xfs_ail *ailp,
xfs_log_item_t *lip,
int shutdown_type) __releases(ailp->xa_lock)
{
xfs_trans_ail_delete_bulk(ailp, &lip, 1, shutdown_type);
}
bool xfs_ail_delete_one(struct xfs_ail *ailp, struct xfs_log_item *lip);
void xfs_trans_ail_delete(struct xfs_ail *ailp, struct xfs_log_item *lip,
int shutdown_type) __releases(ailp->xa_lock);
static inline void
xfs_trans_ail_remove(
......
/*
* FS_IOC_GETFSMAP ioctl infrastructure.
*
* Copyright (C) 2017 Oracle. All Rights Reserved.
*
* Author: Darrick J. Wong <darrick.wong@oracle.com>
*/
#ifndef _LINUX_FSMAP_H
#define _LINUX_FSMAP_H
#include <linux/types.h>
/*
* Structure for FS_IOC_GETFSMAP.
*
* The memory layout for this call are the scalar values defined in
* struct fsmap_head, followed by two struct fsmap that describe
* the lower and upper bound of mappings to return, followed by an
* array of struct fsmap mappings.
*
* fmh_iflags control the output of the call, whereas fmh_oflags report
* on the overall record output. fmh_count should be set to the
* length of the fmh_recs array, and fmh_entries will be set to the
* number of entries filled out during each call. If fmh_count is
* zero, the number of reverse mappings will be returned in
* fmh_entries, though no mappings will be returned. fmh_reserved
* must be set to zero.
*
* The two elements in the fmh_keys array are used to constrain the
* output. The first element in the array should represent the
* lowest disk mapping ("low key") that the user wants to learn
* about. If this value is all zeroes, the filesystem will return
* the first entry it knows about. For a subsequent call, the
* contents of fsmap_head.fmh_recs[fsmap_head.fmh_count - 1] should be
* copied into fmh_keys[0] to have the kernel start where it left off.
*
* The second element in the fmh_keys array should represent the
* highest disk mapping ("high key") that the user wants to learn
* about. If this value is all ones, the filesystem will not stop
* until it runs out of mapping to return or runs out of space in
* fmh_recs.
*
* fmr_device can be either a 32-bit cookie representing a device, or
* a 32-bit dev_t if the FMH_OF_DEV_T flag is set. fmr_physical,
* fmr_offset, and fmr_length are expressed in units of bytes.
* fmr_owner is either an inode number, or a special value if
* FMR_OF_SPECIAL_OWNER is set in fmr_flags.
*/
struct fsmap {
__u32 fmr_device; /* device id */
__u32 fmr_flags; /* mapping flags */
__u64 fmr_physical; /* device offset of segment */
__u64 fmr_owner; /* owner id */
__u64 fmr_offset; /* file offset of segment */
__u64 fmr_length; /* length of segment */
__u64 fmr_reserved[3]; /* must be zero */
};
struct fsmap_head {
__u32 fmh_iflags; /* control flags */
__u32 fmh_oflags; /* output flags */
__u32 fmh_count; /* # of entries in array incl. input */
__u32 fmh_entries; /* # of entries filled in (output). */
__u64 fmh_reserved[6]; /* must be zero */
struct fsmap fmh_keys[2]; /* low and high keys for the mapping search */
struct fsmap fmh_recs[]; /* returned records */
};
/* Size of an fsmap_head with room for nr records. */
static inline size_t
fsmap_sizeof(
unsigned int nr)
{
return sizeof(struct fsmap_head) + nr * sizeof(struct fsmap);
}
/* Start the next fsmap query at the end of the current query results. */
static inline void
fsmap_advance(
struct fsmap_head *head)
{
head->fmh_keys[0] = head->fmh_recs[head->fmh_entries - 1];
}
/* fmh_iflags values - set by FS_IOC_GETFSMAP caller in the header. */
/* no flags defined yet */
#define FMH_IF_VALID 0
/* fmh_oflags values - returned in the header segment only. */
#define FMH_OF_DEV_T 0x1 /* fmr_device values will be dev_t */
/* fmr_flags values - returned for each non-header segment */
#define FMR_OF_PREALLOC 0x1 /* segment = unwritten pre-allocation */
#define FMR_OF_ATTR_FORK 0x2 /* segment = attribute fork */
#define FMR_OF_EXTENT_MAP 0x4 /* segment = extent map */
#define FMR_OF_SHARED 0x8 /* segment = shared with another file */
#define FMR_OF_SPECIAL_OWNER 0x10 /* owner is a special value */
#define FMR_OF_LAST 0x20 /* segment is the last in the FS */
/* Each FS gets to define its own special owner codes. */
#define FMR_OWNER(type, code) (((__u64)type << 32) | \
((__u64)code & 0xFFFFFFFFULL))
#define FMR_OWNER_TYPE(owner) ((__u32)((__u64)owner >> 32))
#define FMR_OWNER_CODE(owner) ((__u32)(((__u64)owner & 0xFFFFFFFFULL)))
#define FMR_OWN_FREE FMR_OWNER(0, 1) /* free space */
#define FMR_OWN_UNKNOWN FMR_OWNER(0, 2) /* unknown owner */
#define FMR_OWN_METADATA FMR_OWNER(0, 3) /* metadata */
#define FS_IOC_GETFSMAP _IOWR('X', 59, struct fsmap_head)
#endif /* _LINUX_FSMAP_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