2023-04-01 14:37:19

by JunChao Sun

[permalink] [raw]
Subject: [PATCH] ext4: fix performance issue of xattr when expanding inode

Currently ext4 will delete ea entry from ibody and recreate ea entry
which store the same value when expanding inode. The main performance
issue is caused by the fact that ext4 will destroy and recreate the
ea inode, and such behavior is redundant.

The patch is a bit ugly, because ext4_xattr_set_entry() contains the
creating,deleting,replacing of xattr without external intervention,
this looks good. But the movement of ea entry from ibody to block
breaks this, so add an argument for ext4_xattr_set_entry() for this
break, and then ext4_xattr_block_set() will reuse the ea_inode instead
of recreating an ea_inode which store the same value.

Signed-off-by: JunChao Sun <[email protected]>
---
fs/ext4/xattr.c | 99 ++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 81 insertions(+), 18 deletions(-)

diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 767454d74cd6..a1dee1380e47 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -1634,7 +1634,7 @@ static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode,
static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
struct ext4_xattr_search *s,
handle_t *handle, struct inode *inode,
- bool is_block)
+ bool is_block, struct inode *mv_ea_inode)
{
struct ext4_xattr_entry *last, *next;
struct ext4_xattr_entry *here = s->here;
@@ -1727,7 +1727,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
goto out;
}
}
- if (i->value && in_inode) {
+ if (i->value && in_inode && !mv_ea_inode) {
WARN_ON_ONCE(!i->value_len);

ret = ext4_xattr_inode_alloc_quota(inode, i->value_len);
@@ -1819,7 +1819,9 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,

if (i->value) {
/* Insert new value. */
- if (in_inode) {
+ if (in_inode && mv_ea_inode) {
+ here->e_value_inum = cpu_to_le32(mv_ea_inode->i_ino);
+ } else if (in_inode) {
here->e_value_inum = cpu_to_le32(new_ea_inode->i_ino);
} else if (i->value_len) {
void *val = s->base + min_offs - new_size;
@@ -1838,7 +1840,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i,
}

update_hash:
- if (i->value) {
+ if (i->value && !mv_ea_inode) {
__le32 hash = 0;

/* Entry hash calculation. */
@@ -1922,7 +1924,7 @@ ext4_xattr_block_find(struct inode *inode, struct ext4_xattr_info *i,
static int
ext4_xattr_block_set(handle_t *handle, struct inode *inode,
struct ext4_xattr_info *i,
- struct ext4_xattr_block_find *bs)
+ struct ext4_xattr_block_find *bs, struct inode *mv_ea_inode)
{
struct super_block *sb = inode->i_sb;
struct buffer_head *new_bh = NULL;
@@ -1972,7 +1974,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
}
ea_bdebug(bs->bh, "modifying in-place");
error = ext4_xattr_set_entry(i, s, handle, inode,
- true /* is_block */);
+ true /* is_block */, NULL);
ext4_xattr_block_csum_set(inode, bs->bh);
unlock_buffer(bs->bh);
if (error == -EFSCORRUPTED)
@@ -2040,7 +2042,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
s->end = s->base + sb->s_blocksize;
}

- error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */);
+ error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */, mv_ea_inode);
if (error == -EFSCORRUPTED)
goto bad_block;
if (error)
@@ -2286,7 +2288,7 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
return -ENOSPC;

- error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */);
+ error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */, NULL);
if (error)
return error;
header = IHDR(inode, ext4_raw_inode(&is->iloc));
@@ -2429,7 +2431,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
if (!is.s.not_found)
error = ext4_xattr_ibody_set(handle, inode, &i, &is);
else if (!bs.s.not_found)
- error = ext4_xattr_block_set(handle, inode, &i, &bs);
+ error = ext4_xattr_block_set(handle, inode, &i, &bs, NULL);
} else {
error = 0;
/* Xattr value did not change? Save us some work and bail out */
@@ -2446,7 +2448,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
error = ext4_xattr_ibody_set(handle, inode, &i, &is);
if (!error && !bs.s.not_found) {
i.value = NULL;
- error = ext4_xattr_block_set(handle, inode, &i, &bs);
+ error = ext4_xattr_block_set(handle, inode, &i, &bs, NULL);
} else if (error == -ENOSPC) {
if (EXT4_I(inode)->i_file_acl && !bs.s.base) {
brelse(bs.bh);
@@ -2455,7 +2457,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
if (error)
goto cleanup;
}
- error = ext4_xattr_block_set(handle, inode, &i, &bs);
+ error = ext4_xattr_block_set(handle, inode, &i, &bs, NULL);
if (!error && !is.s.not_found) {
i.value = NULL;
error = ext4_xattr_ibody_set(handle, inode, &i,
@@ -2615,6 +2617,10 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
.in_inode = !!entry->e_value_inum,
};
struct ext4_xattr_ibody_header *header = IHDR(inode, raw_inode);
+ struct ext4_xattr_entry *here = NULL, *last = NULL, *next = NULL;
+ struct inode *old_ea_inode = NULL;
+ size_t name_size = EXT4_XATTR_LEN(entry->e_name_len);
+ size_t min_offs;
int error;

is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
@@ -2660,20 +2666,76 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,

i.value = buffer;
i.value_len = value_size;
+ here = is->s.here;
+ last = is->s.first;
+ min_offs = is->s.end - is->s.base;
+ /* Compute min_offs and last entry */
+ for (; !IS_LAST_ENTRY(last); last = next) {
+ next = EXT4_XATTR_NEXT(last);
+ if ((void *)next >= is->s.end) {
+ EXT4_ERROR_INODE(inode, "corrupted xattr entries");
+ error = -EFSCORRUPTED;
+ goto out;
+ }
+ if (!last->e_value_inum && last->e_value_size) {
+ size_t offs = le16_to_cpu(last->e_value_offs);
+
+ if (offs < min_offs)
+ min_offs = offs;
+ }
+ }
+
+ /* Remove the name in ibody */
+ last = ENTRY((void *)last - name_size);
+ memmove(here, (void *)here + name_size,
+ (void *)last - (void *)here + sizeof(__u32));
+ memset(last, 0, name_size);
+
+ /* Get the ea_inode which store the old value */
+ if (here->e_value_inum) {
+ error = ext4_xattr_inode_iget(inode,
+ le32_to_cpu(here->e_value_inum),
+ le32_to_cpu(here->e_hash),
+ &old_ea_inode);
+ if (error) {
+ old_ea_inode = NULL;
+ goto out;
+ }
+ } else if (here->e_value_size) {
+ /* Remove the old value in ibody */
+ void *first_val = is->s.base + min_offs;
+ void *rm_val = is->s.base + here->e_value_offs;
+ size_t rm_size = EXT4_XATTR_SIZE(le32_to_cpu(here->e_value_size));
+ size_t offs = here->e_value_offs;
+
+ memmove(first_val + rm_size, first_val, rm_val - first_val);
+ memset(first_val, 0, rm_size);
+ min_offs += rm_size;
+
+ /* Adjust all value offsets */
+ last = is->s.first;
+ while (!IS_LAST_ENTRY(last)) {
+ size_t o = le16_to_cpu(last->e_value_offs);
+
+ if (!last->e_value_inum &&
+ last->e_value_size && o < offs)
+ last->e_value_offs = cpu_to_le16(o + rm_size);
+ last = EXT4_XATTR_NEXT(last);
+ }
+ }
+
error = ext4_xattr_block_find(inode, &i, bs);
if (error)
goto out;

- /* Move ea entry from the inode into the block */
- error = ext4_xattr_block_set(handle, inode, &i, bs);
+ /*
+ * Move ea entry from the inode into the block, and do not need to
+ * recreate an ea_inode that store the same value.
+ */
+ error = ext4_xattr_block_set(handle, inode, &i, bs, old_ea_inode);
if (error)
goto out;

- /* Remove the chosen entry from the inode */
- i.value = NULL;
- i.value_len = 0;
- error = ext4_xattr_ibody_set(handle, inode, &i, is);
-
out:
kfree(b_entry_name);
if (entry->e_value_inum && buffer)
@@ -2684,6 +2746,7 @@ static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
brelse(bs->bh);
kfree(is);
kfree(bs);
+ iput(old_ea_inode);

return error;
}
--
2.17.1


2023-04-01 20:50:41

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH] ext4: fix performance issue of xattr when expanding inode

Hi JunChao,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on tytso-ext4/dev]
[also build test WARNING on linus/master v6.3-rc4 next-20230331]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/JunChao-Sun/ext4-fix-performance-issue-of-xattr-when-expanding-inode/20230401-223339
base: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
patch link: https://lore.kernel.org/r/20230401143244.70332-1-sunjunchao2870%40gmail.com
patch subject: [PATCH] ext4: fix performance issue of xattr when expanding inode
config: mips-randconfig-s033-20230402 (https://download.01.org/0day-ci/archive/20230402/[email protected]/config)
compiler: mips64el-linux-gcc (GCC) 12.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/715c6d399c95e6914f37b3dfc08bb88a9d6b2120
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review JunChao-Sun/ext4-fix-performance-issue-of-xattr-when-expanding-inode/20230401-223339
git checkout 715c6d399c95e6914f37b3dfc08bb88a9d6b2120
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=mips olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=mips SHELL=/bin/bash fs/ext4/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

sparse warnings: (new ones prefixed by >>)
fs/ext4/xattr.c:2707:49: sparse: sparse: restricted __le16 degrades to integer
>> fs/ext4/xattr.c:2709:35: sparse: sparse: incorrect type in initializer (different base types) @@ expected unsigned long [usertype] offs @@ got restricted __le16 [usertype] e_value_offs @@
fs/ext4/xattr.c:2709:35: sparse: expected unsigned long [usertype] offs
fs/ext4/xattr.c:2709:35: sparse: got restricted __le16 [usertype] e_value_offs

vim +2709 fs/ext4/xattr.c

2601
2602 /*
2603 * Move xattr pointed to by 'entry' from inode into external xattr block
2604 */
2605 static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
2606 struct ext4_inode *raw_inode,
2607 struct ext4_xattr_entry *entry)
2608 {
2609 struct ext4_xattr_ibody_find *is = NULL;
2610 struct ext4_xattr_block_find *bs = NULL;
2611 char *buffer = NULL, *b_entry_name = NULL;
2612 size_t value_size = le32_to_cpu(entry->e_value_size);
2613 struct ext4_xattr_info i = {
2614 .value = NULL,
2615 .value_len = 0,
2616 .name_index = entry->e_name_index,
2617 .in_inode = !!entry->e_value_inum,
2618 };
2619 struct ext4_xattr_ibody_header *header = IHDR(inode, raw_inode);
2620 struct ext4_xattr_entry *here = NULL, *last = NULL, *next = NULL;
2621 struct inode *old_ea_inode = NULL;
2622 size_t name_size = EXT4_XATTR_LEN(entry->e_name_len);
2623 size_t min_offs;
2624 int error;
2625
2626 is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
2627 bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS);
2628 b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);
2629 if (!is || !bs || !b_entry_name) {
2630 error = -ENOMEM;
2631 goto out;
2632 }
2633
2634 is->s.not_found = -ENODATA;
2635 bs->s.not_found = -ENODATA;
2636 is->iloc.bh = NULL;
2637 bs->bh = NULL;
2638
2639 /* Save the entry name and the entry value */
2640 if (entry->e_value_inum) {
2641 buffer = kvmalloc(value_size, GFP_NOFS);
2642 if (!buffer) {
2643 error = -ENOMEM;
2644 goto out;
2645 }
2646
2647 error = ext4_xattr_inode_get(inode, entry, buffer, value_size);
2648 if (error)
2649 goto out;
2650 } else {
2651 size_t value_offs = le16_to_cpu(entry->e_value_offs);
2652 buffer = (void *)IFIRST(header) + value_offs;
2653 }
2654
2655 memcpy(b_entry_name, entry->e_name, entry->e_name_len);
2656 b_entry_name[entry->e_name_len] = '\0';
2657 i.name = b_entry_name;
2658
2659 error = ext4_get_inode_loc(inode, &is->iloc);
2660 if (error)
2661 goto out;
2662
2663 error = ext4_xattr_ibody_find(inode, &i, is);
2664 if (error)
2665 goto out;
2666
2667 i.value = buffer;
2668 i.value_len = value_size;
2669 here = is->s.here;
2670 last = is->s.first;
2671 min_offs = is->s.end - is->s.base;
2672 /* Compute min_offs and last entry */
2673 for (; !IS_LAST_ENTRY(last); last = next) {
2674 next = EXT4_XATTR_NEXT(last);
2675 if ((void *)next >= is->s.end) {
2676 EXT4_ERROR_INODE(inode, "corrupted xattr entries");
2677 error = -EFSCORRUPTED;
2678 goto out;
2679 }
2680 if (!last->e_value_inum && last->e_value_size) {
2681 size_t offs = le16_to_cpu(last->e_value_offs);
2682
2683 if (offs < min_offs)
2684 min_offs = offs;
2685 }
2686 }
2687
2688 /* Remove the name in ibody */
2689 last = ENTRY((void *)last - name_size);
2690 memmove(here, (void *)here + name_size,
2691 (void *)last - (void *)here + sizeof(__u32));
2692 memset(last, 0, name_size);
2693
2694 /* Get the ea_inode which store the old value */
2695 if (here->e_value_inum) {
2696 error = ext4_xattr_inode_iget(inode,
2697 le32_to_cpu(here->e_value_inum),
2698 le32_to_cpu(here->e_hash),
2699 &old_ea_inode);
2700 if (error) {
2701 old_ea_inode = NULL;
2702 goto out;
2703 }
2704 } else if (here->e_value_size) {
2705 /* Remove the old value in ibody */
2706 void *first_val = is->s.base + min_offs;
2707 void *rm_val = is->s.base + here->e_value_offs;
2708 size_t rm_size = EXT4_XATTR_SIZE(le32_to_cpu(here->e_value_size));
> 2709 size_t offs = here->e_value_offs;
2710
2711 memmove(first_val + rm_size, first_val, rm_val - first_val);
2712 memset(first_val, 0, rm_size);
2713 min_offs += rm_size;
2714
2715 /* Adjust all value offsets */
2716 last = is->s.first;
2717 while (!IS_LAST_ENTRY(last)) {
2718 size_t o = le16_to_cpu(last->e_value_offs);
2719
2720 if (!last->e_value_inum &&
2721 last->e_value_size && o < offs)
2722 last->e_value_offs = cpu_to_le16(o + rm_size);
2723 last = EXT4_XATTR_NEXT(last);
2724 }
2725 }
2726
2727 error = ext4_xattr_block_find(inode, &i, bs);
2728 if (error)
2729 goto out;
2730
2731 /*
2732 * Move ea entry from the inode into the block, and do not need to
2733 * recreate an ea_inode that store the same value.
2734 */
2735 error = ext4_xattr_block_set(handle, inode, &i, bs, old_ea_inode);
2736 if (error)
2737 goto out;
2738
2739 out:
2740 kfree(b_entry_name);
2741 if (entry->e_value_inum && buffer)
2742 kvfree(buffer);
2743 if (is)
2744 brelse(is->iloc.bh);
2745 if (bs)
2746 brelse(bs->bh);
2747 kfree(is);
2748 kfree(bs);
2749 iput(old_ea_inode);
2750
2751 return error;
2752 }
2753

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

2023-04-01 22:04:41

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH] ext4: fix performance issue of xattr when expanding inode

Hi JunChao,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on tytso-ext4/dev]
[also build test WARNING on linus/master v6.3-rc4 next-20230331]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/JunChao-Sun/ext4-fix-performance-issue-of-xattr-when-expanding-inode/20230401-223339
base: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
patch link: https://lore.kernel.org/r/20230401143244.70332-1-sunjunchao2870%40gmail.com
patch subject: [PATCH] ext4: fix performance issue of xattr when expanding inode
config: sparc-randconfig-s033-20230402 (https://download.01.org/0day-ci/archive/20230402/[email protected]/config)
compiler: sparc-linux-gcc (GCC) 12.1.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/715c6d399c95e6914f37b3dfc08bb88a9d6b2120
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review JunChao-Sun/ext4-fix-performance-issue-of-xattr-when-expanding-inode/20230401-223339
git checkout 715c6d399c95e6914f37b3dfc08bb88a9d6b2120
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=sparc olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=sparc SHELL=/bin/bash fs/ext4/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

sparse warnings: (new ones prefixed by >>)
>> fs/ext4/xattr.c:2707:49: sparse: sparse: restricted __le16 degrades to integer
>> fs/ext4/xattr.c:2709:35: sparse: sparse: incorrect type in initializer (different base types) @@ expected unsigned int [usertype] offs @@ got restricted __le16 [usertype] e_value_offs @@
fs/ext4/xattr.c:2709:35: sparse: expected unsigned int [usertype] offs
fs/ext4/xattr.c:2709:35: sparse: got restricted __le16 [usertype] e_value_offs

vim +2707 fs/ext4/xattr.c

2601
2602 /*
2603 * Move xattr pointed to by 'entry' from inode into external xattr block
2604 */
2605 static int ext4_xattr_move_to_block(handle_t *handle, struct inode *inode,
2606 struct ext4_inode *raw_inode,
2607 struct ext4_xattr_entry *entry)
2608 {
2609 struct ext4_xattr_ibody_find *is = NULL;
2610 struct ext4_xattr_block_find *bs = NULL;
2611 char *buffer = NULL, *b_entry_name = NULL;
2612 size_t value_size = le32_to_cpu(entry->e_value_size);
2613 struct ext4_xattr_info i = {
2614 .value = NULL,
2615 .value_len = 0,
2616 .name_index = entry->e_name_index,
2617 .in_inode = !!entry->e_value_inum,
2618 };
2619 struct ext4_xattr_ibody_header *header = IHDR(inode, raw_inode);
2620 struct ext4_xattr_entry *here = NULL, *last = NULL, *next = NULL;
2621 struct inode *old_ea_inode = NULL;
2622 size_t name_size = EXT4_XATTR_LEN(entry->e_name_len);
2623 size_t min_offs;
2624 int error;
2625
2626 is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
2627 bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS);
2628 b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);
2629 if (!is || !bs || !b_entry_name) {
2630 error = -ENOMEM;
2631 goto out;
2632 }
2633
2634 is->s.not_found = -ENODATA;
2635 bs->s.not_found = -ENODATA;
2636 is->iloc.bh = NULL;
2637 bs->bh = NULL;
2638
2639 /* Save the entry name and the entry value */
2640 if (entry->e_value_inum) {
2641 buffer = kvmalloc(value_size, GFP_NOFS);
2642 if (!buffer) {
2643 error = -ENOMEM;
2644 goto out;
2645 }
2646
2647 error = ext4_xattr_inode_get(inode, entry, buffer, value_size);
2648 if (error)
2649 goto out;
2650 } else {
2651 size_t value_offs = le16_to_cpu(entry->e_value_offs);
2652 buffer = (void *)IFIRST(header) + value_offs;
2653 }
2654
2655 memcpy(b_entry_name, entry->e_name, entry->e_name_len);
2656 b_entry_name[entry->e_name_len] = '\0';
2657 i.name = b_entry_name;
2658
2659 error = ext4_get_inode_loc(inode, &is->iloc);
2660 if (error)
2661 goto out;
2662
2663 error = ext4_xattr_ibody_find(inode, &i, is);
2664 if (error)
2665 goto out;
2666
2667 i.value = buffer;
2668 i.value_len = value_size;
2669 here = is->s.here;
2670 last = is->s.first;
2671 min_offs = is->s.end - is->s.base;
2672 /* Compute min_offs and last entry */
2673 for (; !IS_LAST_ENTRY(last); last = next) {
2674 next = EXT4_XATTR_NEXT(last);
2675 if ((void *)next >= is->s.end) {
2676 EXT4_ERROR_INODE(inode, "corrupted xattr entries");
2677 error = -EFSCORRUPTED;
2678 goto out;
2679 }
2680 if (!last->e_value_inum && last->e_value_size) {
2681 size_t offs = le16_to_cpu(last->e_value_offs);
2682
2683 if (offs < min_offs)
2684 min_offs = offs;
2685 }
2686 }
2687
2688 /* Remove the name in ibody */
2689 last = ENTRY((void *)last - name_size);
2690 memmove(here, (void *)here + name_size,
2691 (void *)last - (void *)here + sizeof(__u32));
2692 memset(last, 0, name_size);
2693
2694 /* Get the ea_inode which store the old value */
2695 if (here->e_value_inum) {
2696 error = ext4_xattr_inode_iget(inode,
2697 le32_to_cpu(here->e_value_inum),
2698 le32_to_cpu(here->e_hash),
2699 &old_ea_inode);
2700 if (error) {
2701 old_ea_inode = NULL;
2702 goto out;
2703 }
2704 } else if (here->e_value_size) {
2705 /* Remove the old value in ibody */
2706 void *first_val = is->s.base + min_offs;
> 2707 void *rm_val = is->s.base + here->e_value_offs;
2708 size_t rm_size = EXT4_XATTR_SIZE(le32_to_cpu(here->e_value_size));
> 2709 size_t offs = here->e_value_offs;
2710
2711 memmove(first_val + rm_size, first_val, rm_val - first_val);
2712 memset(first_val, 0, rm_size);
2713 min_offs += rm_size;
2714
2715 /* Adjust all value offsets */
2716 last = is->s.first;
2717 while (!IS_LAST_ENTRY(last)) {
2718 size_t o = le16_to_cpu(last->e_value_offs);
2719
2720 if (!last->e_value_inum &&
2721 last->e_value_size && o < offs)
2722 last->e_value_offs = cpu_to_le16(o + rm_size);
2723 last = EXT4_XATTR_NEXT(last);
2724 }
2725 }
2726
2727 error = ext4_xattr_block_find(inode, &i, bs);
2728 if (error)
2729 goto out;
2730
2731 /*
2732 * Move ea entry from the inode into the block, and do not need to
2733 * recreate an ea_inode that store the same value.
2734 */
2735 error = ext4_xattr_block_set(handle, inode, &i, bs, old_ea_inode);
2736 if (error)
2737 goto out;
2738
2739 out:
2740 kfree(b_entry_name);
2741 if (entry->e_value_inum && buffer)
2742 kvfree(buffer);
2743 if (is)
2744 brelse(is->iloc.bh);
2745 if (bs)
2746 brelse(bs->bh);
2747 kfree(is);
2748 kfree(bs);
2749 iput(old_ea_inode);
2750
2751 return error;
2752 }
2753

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests