xfs: remove support for inlining data/extents into the inode fork
Supporting a small bit of data inside the inode fork blows up the fork size a lot, removing the 32 bytes of inline data halves the effective size of the inode fork (and it still has a lot of unused padding left), and the performance of a single kmalloc doesn't show up compared to the size to read an inode or create one. It also simplifies the fork management code a lot. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
This commit is contained in:
parent
b121459c7a
commit
43518812d2
3 changed files with 13 additions and 198 deletions
|
@ -269,19 +269,14 @@ xfs_init_local_fork(
|
|||
if (zero_terminate)
|
||||
mem_size++;
|
||||
|
||||
if (size == 0)
|
||||
ifp->if_u1.if_data = NULL;
|
||||
else if (mem_size <= sizeof(ifp->if_u2.if_inline_data))
|
||||
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
|
||||
else {
|
||||
if (size) {
|
||||
real_size = roundup(mem_size, 4);
|
||||
ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS);
|
||||
}
|
||||
|
||||
if (size) {
|
||||
memcpy(ifp->if_u1.if_data, data, size);
|
||||
if (zero_terminate)
|
||||
ifp->if_u1.if_data[size] = '\0';
|
||||
} else {
|
||||
ifp->if_u1.if_data = NULL;
|
||||
}
|
||||
|
||||
ifp->if_bytes = size;
|
||||
|
@ -292,13 +287,6 @@ xfs_init_local_fork(
|
|||
|
||||
/*
|
||||
* The file is in-lined in the on-disk inode.
|
||||
* If it fits into if_inline_data, then copy
|
||||
* it there, otherwise allocate a buffer for it
|
||||
* and copy the data there. Either way, set
|
||||
* if_data to point at the data.
|
||||
* If we allocate a buffer for the data, make
|
||||
* sure that its size is a multiple of 4 and
|
||||
* record the real size in i_real_bytes.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_iformat_local(
|
||||
|
@ -328,9 +316,7 @@ 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.
|
||||
* inode.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_iformat_extents(
|
||||
|
@ -362,8 +348,6 @@ xfs_iformat_extents(
|
|||
ifp->if_real_bytes = 0;
|
||||
if (nex == 0)
|
||||
ifp->if_u1.if_extents = NULL;
|
||||
else if (nex <= XFS_INLINE_EXTS)
|
||||
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
|
||||
else
|
||||
xfs_iext_add(ifp, 0, nex);
|
||||
|
||||
|
@ -618,26 +602,9 @@ xfs_idata_realloc(
|
|||
ASSERT(new_size >= 0);
|
||||
|
||||
if (new_size == 0) {
|
||||
if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
|
||||
kmem_free(ifp->if_u1.if_data);
|
||||
}
|
||||
kmem_free(ifp->if_u1.if_data);
|
||||
ifp->if_u1.if_data = NULL;
|
||||
real_size = 0;
|
||||
} else if (new_size <= sizeof(ifp->if_u2.if_inline_data)) {
|
||||
/*
|
||||
* If the valid extents/data can fit in if_inline_ext/data,
|
||||
* copy them from the malloc'd vector and free it.
|
||||
*/
|
||||
if (ifp->if_u1.if_data == NULL) {
|
||||
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
|
||||
} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
|
||||
ASSERT(ifp->if_real_bytes != 0);
|
||||
memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
|
||||
new_size);
|
||||
kmem_free(ifp->if_u1.if_data);
|
||||
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
|
||||
}
|
||||
real_size = 0;
|
||||
} else {
|
||||
/*
|
||||
* Stuck with malloc/realloc.
|
||||
|
@ -651,7 +618,7 @@ xfs_idata_realloc(
|
|||
ASSERT(ifp->if_real_bytes == 0);
|
||||
ifp->if_u1.if_data = kmem_alloc(real_size,
|
||||
KM_SLEEP | KM_NOFS);
|
||||
} else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
|
||||
} else {
|
||||
/*
|
||||
* Only do the realloc if the underlying size
|
||||
* is really changing.
|
||||
|
@ -662,12 +629,6 @@ xfs_idata_realloc(
|
|||
real_size,
|
||||
KM_SLEEP | KM_NOFS);
|
||||
}
|
||||
} else {
|
||||
ASSERT(ifp->if_real_bytes == 0);
|
||||
ifp->if_u1.if_data = kmem_alloc(real_size,
|
||||
KM_SLEEP | KM_NOFS);
|
||||
memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data,
|
||||
ifp->if_bytes);
|
||||
}
|
||||
}
|
||||
ifp->if_real_bytes = real_size;
|
||||
|
@ -695,8 +656,7 @@ xfs_idestroy_fork(
|
|||
* so check and free it up if we do.
|
||||
*/
|
||||
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
|
||||
if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
|
||||
(ifp->if_u1.if_data != NULL)) {
|
||||
if (ifp->if_u1.if_data != NULL) {
|
||||
ASSERT(ifp->if_real_bytes != 0);
|
||||
kmem_free(ifp->if_u1.if_data);
|
||||
ifp->if_u1.if_data = NULL;
|
||||
|
@ -704,13 +664,11 @@ xfs_idestroy_fork(
|
|||
}
|
||||
} else if ((ifp->if_flags & XFS_IFEXTENTS) &&
|
||||
((ifp->if_flags & XFS_IFEXTIREC) ||
|
||||
((ifp->if_u1.if_extents != NULL) &&
|
||||
(ifp->if_u1.if_extents != ifp->if_u2.if_inline_ext)))) {
|
||||
(ifp->if_u1.if_extents != NULL))) {
|
||||
ASSERT(ifp->if_real_bytes != 0);
|
||||
xfs_iext_destroy(ifp);
|
||||
}
|
||||
ASSERT(ifp->if_u1.if_extents == NULL ||
|
||||
ifp->if_u1.if_extents == ifp->if_u2.if_inline_ext);
|
||||
ASSERT(ifp->if_u1.if_extents == NULL);
|
||||
ASSERT(ifp->if_real_bytes == 0);
|
||||
if (whichfork == XFS_ATTR_FORK) {
|
||||
kmem_zone_free(xfs_ifork_zone, ip->i_afp);
|
||||
|
@ -943,28 +901,14 @@ xfs_iext_add(
|
|||
ASSERT((idx >= 0) && (idx <= nextents));
|
||||
byte_diff = ext_diff * sizeof(xfs_bmbt_rec_t);
|
||||
new_size = ifp->if_bytes + byte_diff;
|
||||
|
||||
/*
|
||||
* If the new number of extents (nextents + ext_diff)
|
||||
* fits inside the inode, then continue to use the inline
|
||||
* extent buffer.
|
||||
*/
|
||||
if (nextents + ext_diff <= XFS_INLINE_EXTS) {
|
||||
if (idx < nextents) {
|
||||
memmove(&ifp->if_u2.if_inline_ext[idx + ext_diff],
|
||||
&ifp->if_u2.if_inline_ext[idx],
|
||||
(nextents - idx) * sizeof(xfs_bmbt_rec_t));
|
||||
memset(&ifp->if_u2.if_inline_ext[idx], 0, byte_diff);
|
||||
}
|
||||
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
|
||||
ifp->if_real_bytes = 0;
|
||||
}
|
||||
/*
|
||||
* Otherwise use a linear (direct) extent list.
|
||||
* Use a linear (direct) extent list.
|
||||
* If the extents are currently inside the inode,
|
||||
* xfs_iext_realloc_direct will switch us from
|
||||
* inline to direct extent allocation mode.
|
||||
*/
|
||||
else if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
|
||||
if (nextents + ext_diff <= XFS_LINEAR_EXTS) {
|
||||
xfs_iext_realloc_direct(ifp, new_size);
|
||||
if (idx < nextents) {
|
||||
memmove(&ifp->if_u1.if_extents[idx + ext_diff],
|
||||
|
@ -1172,43 +1116,10 @@ xfs_iext_remove(
|
|||
xfs_iext_remove_indirect(ifp, cur->idx, ext_diff);
|
||||
} else if (ifp->if_real_bytes) {
|
||||
xfs_iext_remove_direct(ifp, cur->idx, ext_diff);
|
||||
} else {
|
||||
xfs_iext_remove_inline(ifp, cur->idx, ext_diff);
|
||||
}
|
||||
ifp->if_bytes = new_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* This removes ext_diff extents from the inline buffer, beginning
|
||||
* at extent index idx.
|
||||
*/
|
||||
void
|
||||
xfs_iext_remove_inline(
|
||||
xfs_ifork_t *ifp, /* inode fork pointer */
|
||||
xfs_extnum_t idx, /* index to begin removing exts */
|
||||
int ext_diff) /* number of extents to remove */
|
||||
{
|
||||
int nextents; /* number of extents in file */
|
||||
|
||||
ASSERT(!(ifp->if_flags & XFS_IFEXTIREC));
|
||||
ASSERT(idx < XFS_INLINE_EXTS);
|
||||
nextents = xfs_iext_count(ifp);
|
||||
ASSERT(((nextents - ext_diff) > 0) &&
|
||||
(nextents - ext_diff) < XFS_INLINE_EXTS);
|
||||
|
||||
if (idx + ext_diff < nextents) {
|
||||
memmove(&ifp->if_u2.if_inline_ext[idx],
|
||||
&ifp->if_u2.if_inline_ext[idx + ext_diff],
|
||||
(nextents - (idx + ext_diff)) *
|
||||
sizeof(xfs_bmbt_rec_t));
|
||||
memset(&ifp->if_u2.if_inline_ext[nextents - ext_diff],
|
||||
0, ext_diff * sizeof(xfs_bmbt_rec_t));
|
||||
} else {
|
||||
memset(&ifp->if_u2.if_inline_ext[idx], 0,
|
||||
ext_diff * sizeof(xfs_bmbt_rec_t));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This removes ext_diff extents from a linear (direct) extent list,
|
||||
* beginning at extent index idx. If the extents are being removed
|
||||
|
@ -1351,16 +1262,7 @@ xfs_iext_realloc_direct(
|
|||
/* Free extent records */
|
||||
if (new_size == 0) {
|
||||
xfs_iext_destroy(ifp);
|
||||
}
|
||||
/* Resize direct extent list and zero any new bytes */
|
||||
else if (ifp->if_real_bytes) {
|
||||
/* Check if extents will fit inside the inode */
|
||||
if (new_size <= XFS_INLINE_EXTS * sizeof(xfs_bmbt_rec_t)) {
|
||||
xfs_iext_direct_to_inline(ifp, new_size /
|
||||
(uint)sizeof(xfs_bmbt_rec_t));
|
||||
ifp->if_bytes = new_size;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!is_power_of_2(new_size)){
|
||||
rnew_size = roundup_pow_of_two(new_size);
|
||||
}
|
||||
|
@ -1375,63 +1277,10 @@ xfs_iext_realloc_direct(
|
|||
rnew_size - ifp->if_real_bytes);
|
||||
}
|
||||
}
|
||||
/* Switch from the inline extent buffer to a direct extent list */
|
||||
else {
|
||||
if (!is_power_of_2(new_size)) {
|
||||
rnew_size = roundup_pow_of_two(new_size);
|
||||
}
|
||||
xfs_iext_inline_to_direct(ifp, rnew_size);
|
||||
}
|
||||
ifp->if_real_bytes = rnew_size;
|
||||
ifp->if_bytes = new_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch from linear (direct) extent records to inline buffer.
|
||||
*/
|
||||
void
|
||||
xfs_iext_direct_to_inline(
|
||||
xfs_ifork_t *ifp, /* inode fork pointer */
|
||||
xfs_extnum_t nextents) /* number of extents in file */
|
||||
{
|
||||
ASSERT(ifp->if_flags & XFS_IFEXTENTS);
|
||||
ASSERT(nextents <= XFS_INLINE_EXTS);
|
||||
/*
|
||||
* The inline buffer was zeroed when we switched
|
||||
* from inline to direct extent allocation mode,
|
||||
* so we don't need to clear it here.
|
||||
*/
|
||||
memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
|
||||
nextents * sizeof(xfs_bmbt_rec_t));
|
||||
kmem_free(ifp->if_u1.if_extents);
|
||||
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
|
||||
ifp->if_real_bytes = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch from inline buffer to linear (direct) extent records.
|
||||
* new_size should already be rounded up to the next power of 2
|
||||
* by the caller (when appropriate), so use new_size as it is.
|
||||
* However, since new_size may be rounded up, we can't update
|
||||
* if_bytes here. It is the caller's responsibility to update
|
||||
* if_bytes upon return.
|
||||
*/
|
||||
void
|
||||
xfs_iext_inline_to_direct(
|
||||
xfs_ifork_t *ifp, /* inode fork pointer */
|
||||
int new_size) /* number of extents in file */
|
||||
{
|
||||
ifp->if_u1.if_extents = kmem_alloc(new_size, KM_NOFS);
|
||||
memset(ifp->if_u1.if_extents, 0, new_size);
|
||||
if (ifp->if_bytes) {
|
||||
memcpy(ifp->if_u1.if_extents, ifp->if_u2.if_inline_ext,
|
||||
ifp->if_bytes);
|
||||
memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
|
||||
sizeof(xfs_bmbt_rec_t));
|
||||
}
|
||||
ifp->if_real_bytes = new_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize an extent indirection array to new_size bytes.
|
||||
*/
|
||||
|
@ -1511,9 +1360,6 @@ xfs_iext_destroy(
|
|||
xfs_iext_irec_remove_all(ifp);
|
||||
} else if (ifp->if_real_bytes) {
|
||||
kmem_free(ifp->if_u1.if_extents);
|
||||
} else if (ifp->if_bytes) {
|
||||
memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
|
||||
sizeof(xfs_bmbt_rec_t));
|
||||
}
|
||||
ifp->if_u1.if_extents = NULL;
|
||||
ifp->if_real_bytes = 0;
|
||||
|
@ -1708,8 +1554,6 @@ xfs_iext_irec_init(
|
|||
|
||||
if (nextents == 0) {
|
||||
ifp->if_u1.if_extents = kmem_alloc(XFS_IEXT_BUFSZ, KM_NOFS);
|
||||
} else if (!ifp->if_real_bytes) {
|
||||
xfs_iext_inline_to_direct(ifp, XFS_IEXT_BUFSZ);
|
||||
} else if (ifp->if_real_bytes < XFS_IEXT_BUFSZ) {
|
||||
xfs_iext_realloc_direct(ifp, XFS_IEXT_BUFSZ);
|
||||
}
|
||||
|
@ -1829,9 +1673,6 @@ xfs_iext_irec_compact(
|
|||
|
||||
if (nextents == 0) {
|
||||
xfs_iext_destroy(ifp);
|
||||
} else if (nextents <= XFS_INLINE_EXTS) {
|
||||
xfs_iext_indirect_to_direct(ifp);
|
||||
xfs_iext_direct_to_inline(ifp, nextents);
|
||||
} else if (nextents <= XFS_LINEAR_EXTS) {
|
||||
xfs_iext_indirect_to_direct(ifp);
|
||||
} else if (nextents < (nlists * XFS_LINEAR_EXTS) >> 1) {
|
||||
|
|
|
@ -51,8 +51,6 @@ typedef struct xfs_ext_irec {
|
|||
*/
|
||||
#define XFS_IEXT_BUFSZ 4096
|
||||
#define XFS_LINEAR_EXTS (XFS_IEXT_BUFSZ / (uint)sizeof(xfs_bmbt_rec_t))
|
||||
#define XFS_INLINE_EXTS 2
|
||||
#define XFS_INLINE_DATA 32
|
||||
typedef struct xfs_ifork {
|
||||
int if_bytes; /* bytes in if_u1 */
|
||||
int if_real_bytes; /* bytes allocated in if_u1 */
|
||||
|
@ -64,12 +62,6 @@ typedef struct xfs_ifork {
|
|||
xfs_ext_irec_t *if_ext_irec; /* irec map file exts */
|
||||
char *if_data; /* inline file data */
|
||||
} if_u1;
|
||||
union {
|
||||
xfs_bmbt_rec_host_t if_inline_ext[XFS_INLINE_EXTS];
|
||||
/* very small file extents */
|
||||
char if_inline_data[XFS_INLINE_DATA];
|
||||
/* very small file data */
|
||||
} if_u2;
|
||||
} xfs_ifork_t;
|
||||
|
||||
/*
|
||||
|
@ -158,12 +150,9 @@ void xfs_iext_add_indirect_multi(struct xfs_ifork *, int,
|
|||
xfs_extnum_t, int);
|
||||
void xfs_iext_remove(struct xfs_inode *, struct xfs_iext_cursor *,
|
||||
int, int);
|
||||
void xfs_iext_remove_inline(struct xfs_ifork *, xfs_extnum_t, int);
|
||||
void xfs_iext_remove_direct(struct xfs_ifork *, xfs_extnum_t, int);
|
||||
void xfs_iext_remove_indirect(struct xfs_ifork *, xfs_extnum_t, int);
|
||||
void xfs_iext_realloc_direct(struct xfs_ifork *, int);
|
||||
void xfs_iext_direct_to_inline(struct xfs_ifork *, xfs_extnum_t);
|
||||
void xfs_iext_inline_to_direct(struct xfs_ifork *, int);
|
||||
void xfs_iext_destroy(struct xfs_ifork *);
|
||||
struct xfs_bmbt_rec_host *
|
||||
xfs_iext_bno_to_ext(struct xfs_ifork *, xfs_fileoff_t, int *);
|
||||
|
|
|
@ -1709,7 +1709,6 @@ xfs_swap_extent_forks(
|
|||
xfs_filblks_t aforkblks = 0;
|
||||
xfs_filblks_t taforkblks = 0;
|
||||
xfs_extnum_t junk;
|
||||
xfs_extnum_t nextents;
|
||||
uint64_t tmp;
|
||||
int error;
|
||||
|
||||
|
@ -1784,13 +1783,6 @@ xfs_swap_extent_forks(
|
|||
|
||||
switch (ip->i_d.di_format) {
|
||||
case XFS_DINODE_FMT_EXTENTS:
|
||||
/*
|
||||
* If the extents fit in the inode, fix the pointer. Otherwise
|
||||
* it's already NULL or pointing to the extent.
|
||||
*/
|
||||
nextents = xfs_iext_count(&ip->i_df);
|
||||
if (nextents <= XFS_INLINE_EXTS)
|
||||
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
|
||||
(*src_log_flags) |= XFS_ILOG_DEXT;
|
||||
break;
|
||||
case XFS_DINODE_FMT_BTREE:
|
||||
|
@ -1802,13 +1794,6 @@ xfs_swap_extent_forks(
|
|||
|
||||
switch (tip->i_d.di_format) {
|
||||
case XFS_DINODE_FMT_EXTENTS:
|
||||
/*
|
||||
* If the extents fit in the inode, fix the pointer. Otherwise
|
||||
* it's already NULL or pointing to the extent.
|
||||
*/
|
||||
nextents = xfs_iext_count(&tip->i_df);
|
||||
if (nextents <= XFS_INLINE_EXTS)
|
||||
tifp->if_u1.if_extents = tifp->if_u2.if_inline_ext;
|
||||
(*target_log_flags) |= XFS_ILOG_DEXT;
|
||||
break;
|
||||
case XFS_DINODE_FMT_BTREE:
|
||||
|
|
Loading…
Reference in a new issue