Whamcloud - gitweb
LU-9384 ldiskfs: port extra isize patches to sles12sp2
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / sles12sp2 / ext4-fix-xattr-shifting-when-expanding-inodes.patch
1 From e3014d14a81edde488d9a6758eea8afc41752d2d Mon Sep 17 00:00:00 2001
2 From: Jan Kara <jack@suse.cz>
3 Date: Mon, 29 Aug 2016 15:38:11 -0400
4 Subject: [PATCH] ext4: fixup free space calculations when expanding inodes
5
6 Conditions checking whether there is enough free space in an xattr block
7 and when xattr is large enough to make enough space in the inode forgot
8 to account for the fact that inode need not be completely filled up with
9 xattrs. Thus we could move unnecessarily many xattrs out of inode or
10 even falsely claim there is not enough space to expand the inode. We
11 also forgot to update the amount of free space in xattr block when moving
12 more xattrs and thus could decide to move too big xattr resulting in
13 unexpected failure.
14
15 Fix these problems by properly updating free space in the inode and
16 xattr block as we move xattrs. To simplify the math, avoid shifting
17 xattrs after removing each one xattr and instead just shift xattrs only
18 once there is enough free space in the inode.
19
20 Signed-off-by: Jan Kara <jack@suse.cz>
21 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
22 ---
23  fs/ext4/xattr.c | 58 ++++++++++++++++++++++++---------------------------------
24  1 file changed, 24 insertions(+), 34 deletions(-)
25
26 Index: linux-4.4.49-92.14_lustre-vanilla/fs/ext4/xattr.c
27 ===================================================================
28 --- linux-4.4.49-92.14_lustre-vanilla.orig/fs/ext4/xattr.c
29 +++ linux-4.4.49-92.14_lustre-vanilla/fs/ext4/xattr.c
30 @@ -1619,18 +1619,19 @@ retry:
31   */
32  static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry,
33                                      int value_offs_shift, void *to,
34 -                                    void *from, size_t n, int blocksize)
35 +                                    void *from, size_t n)
36  {
37         struct ext4_xattr_entry *last = entry;
38         int new_offs;
39  
40 +       /* We always shift xattr headers further thus offsets get lower */
41 +       BUG_ON(value_offs_shift > 0);
42 +
43         /* Adjust the value offsets of the entries */
44         for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) {
45                 if (!last->e_value_inum && last->e_value_size) {
46                         new_offs = le16_to_cpu(last->e_value_offs) +
47                                                         value_offs_shift;
48 -                       BUG_ON(new_offs + le32_to_cpu(last->e_value_size)
49 -                                > blocksize);
50                         last->e_value_offs = cpu_to_le16(new_offs);
51                 }
52         }
53 @@ -1651,7 +1652,8 @@ int ext4_expand_extra_isize_ea(struct in
54         struct ext4_xattr_ibody_find *is = NULL;
55         struct ext4_xattr_block_find *bs = NULL;
56         char *buffer = NULL, *b_entry_name = NULL;
57 -       size_t min_offs, free;
58 +       size_t min_offs;
59 +       size_t ifree, bfree;
60         int total_ino;
61         void *base, *start, *end;
62         int error = 0, tried_min_extra_isize = 0;
63 @@ -1682,17 +1684,9 @@ retry:
64         last = entry;
65         total_ino = sizeof(struct ext4_xattr_ibody_header);
66  
67 -       free = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
68 -       if (free >= isize_diff) {
69 -               entry = IFIRST(header);
70 -               ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
71 -                               - new_extra_isize, (void *)raw_inode +
72 -                               EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
73 -                               (void *)header, total_ino,
74 -                               inode->i_sb->s_blocksize);
75 -               EXT4_I(inode)->i_extra_isize = new_extra_isize;
76 -               goto out;
77 -       }
78 +       ifree = ext4_xattr_free_space(last, &min_offs, base, &total_ino);
79 +       if (ifree >= isize_diff)
80 +               goto shift;
81  
82         /*
83          * Enough free space isn't available in the inode, check if
84 @@ -1713,8 +1707,8 @@ retry:
85                 first = BFIRST(bh);
86                 end = bh->b_data + bh->b_size;
87                 min_offs = end - base;
88 -               free = ext4_xattr_free_space(first, &min_offs, base, NULL);
89 -               if (free < isize_diff) {
90 +               bfree = ext4_xattr_free_space(first, &min_offs, base, NULL);
91 +               if (bfree + ifree < isize_diff) {
92                         if (!tried_min_extra_isize && s_min_extra_isize) {
93                                 tried_min_extra_isize++;
94                                 new_extra_isize = s_min_extra_isize;
95 @@ -1725,10 +1719,10 @@ retry:
96                         goto cleanup;
97                 }
98         } else {
99 -               free = inode->i_sb->s_blocksize;
100 +               bfree = inode->i_sb->s_blocksize;
101         }
102  
103 -       while (isize_diff > 0) {
104 +       while (isize_diff > ifree) {
105                 size_t offs, size, entry_size;
106                 struct ext4_xattr_entry *small_entry = NULL;
107                 struct ext4_xattr_info i = {
108 @@ -1736,7 +1730,6 @@ retry:
109                         .value_len = 0,
110                 };
111                 unsigned int total_size;  /* EA entry size + value size */
112 -               unsigned int shift_bytes; /* No. of bytes to shift EAs by? */
113                 unsigned int min_total_size = ~0U;
114  
115                 is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS);
116 @@ -1758,8 +1751,9 @@ retry:
117                         total_size =
118                         EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) +
119                                         EXT4_XATTR_LEN(last->e_name_len);
120 -                       if (total_size <= free && total_size < min_total_size) {
121 -                               if (total_size < isize_diff) {
122 +                       if (total_size <= bfree &&
123 +                           total_size < min_total_size) {
124 +                               if (total_size + ifree < isize_diff) {
125                                         small_entry = last;
126                                 } else {
127                                         entry = last;
128 @@ -1788,6 +1782,7 @@ retry:
129                 offs = le16_to_cpu(entry->e_value_offs);
130                 size = le32_to_cpu(entry->e_value_size);
131                 entry_size = EXT4_XATTR_LEN(entry->e_name_len);
132 +               total_size = entry_size + EXT4_XATTR_SIZE(size);
133                 i.name_index = entry->e_name_index,
134                 buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS);
135                 b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS);
136 @@ -1815,21 +1810,8 @@ retry:
137                 if (error)
138                         goto cleanup;
139                 total_ino -= entry_size;
140 -
141 -               entry = IFIRST(header);
142 -               if (entry_size + EXT4_XATTR_SIZE(size) >= isize_diff)
143 -                       shift_bytes = isize_diff;
144 -               else
145 -                       shift_bytes = entry_size + EXT4_XATTR_SIZE(size);
146 -               /* Adjust the offsets and shift the remaining entries ahead */
147 -               ext4_xattr_shift_entries(entry, -shift_bytes,
148 -                       (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
149 -                       EXT4_I(inode)->i_extra_isize + shift_bytes,
150 -                       (void *)header, total_ino, inode->i_sb->s_blocksize);
151 -
152 -               isize_diff -= shift_bytes;
153 -               EXT4_I(inode)->i_extra_isize += shift_bytes;
154 -               header = IHDR(inode, raw_inode);
155 +               ifree += total_size;
156 +               bfree -= total_size;
157  
158                 i.name = b_entry_name;
159                 i.value = buffer;
160 @@ -1850,6 +1832,15 @@ retry:
161                 kfree(is);
162                 kfree(bs);
163         }
164 +
165 +shift:
166 +       /* Adjust the offsets and shift the remaining entries ahead */
167 +       entry = IFIRST(header);
168 +       ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize
169 +                       - new_extra_isize, (void *)raw_inode +
170 +                       EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
171 +                       (void *)header, total_ino);
172 +       EXT4_I(inode)->i_extra_isize = new_extra_isize;
173         brelse(bh);
174  out:
175         ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
176 From 887a9730614727c4fff7cb756711b190593fc1df Mon Sep 17 00:00:00 2001
177 From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
178 Date: Sun, 21 May 2017 22:36:23 -0400
179 Subject: [PATCH] ext4: keep existing extra fields when inode expands
180
181 ext4_expand_extra_isize() should clear only space between old and new
182 size.
183
184 Fixes: 6dd4ee7cab7e # v2.6.23
185 Cc: stable@vger.kernel.org
186 Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru>
187 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
188 ---
189  fs/ext4/inode.c | 5 +++--
190  1 file changed, 3 insertions(+), 2 deletions(-)
191
192 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
193 index 1bd0bfa..7cd99de 100644
194 --- a/fs/ext4/inode.c
195 +++ b/fs/ext4/inode.c
196 @@ -5637,8 +5637,9 @@ static int ext4_expand_extra_isize(struct inode *inode,
197         /* No extended attributes present */
198         if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
199             header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
200 -               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
201 -                       new_extra_isize);
202 +               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE +
203 +                      EXT4_I(inode)->i_extra_isize, 0,
204 +                      new_extra_isize - EXT4_I(inode)->i_extra_isize);
205                 EXT4_I(inode)->i_extra_isize = new_extra_isize;
206                 return 0;
207         }
208 -- 
209 2.9.3
210