]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Tao Ma <tao.ma@oracle.com> |
2 | Subject: ocfs2: Add empty bucket support in xattr. | |
3 | Patch-mainline: 2.6.28 | |
4 | ||
5 | As Mark mentioned, it may be time-consuming when we remove the | |
6 | empty xattr bucket, so this patch try to let empty bucket exist | |
7 | in xattr operation. The modification includes: | |
8 | 1. Remove the functin of bucket and extent record deletion during | |
9 | xattr delete. | |
10 | 2. In xattr set: | |
11 | 1) Don't clean the last entry so that if the bucket is empty, | |
12 | the hash value of the bucket is the hash value of the entry | |
13 | which is deleted last. | |
14 | 2) During insert, if we meet with an empty bucket, just use the | |
15 | 1st entry. | |
16 | 3. In binary search of xattr bucket, use the bucket hash value(which | |
17 | stored in the 1st xattr entry) to find the right place. | |
18 | ||
19 | Signed-off-by: Tao Ma <tao.ma@oracle.com> | |
20 | Signed-off-by: Mark Fasheh <mfasheh@suse.com> | |
21 | --- | |
22 | fs/ocfs2/xattr.c | 197 ++++++++++++------------------------------------------ | |
23 | 1 files changed, 43 insertions(+), 154 deletions(-) | |
24 | ||
25 | Index: linux-2.6.27/fs/ocfs2/xattr.c | |
26 | =================================================================== | |
27 | --- linux-2.6.27.orig/fs/ocfs2/xattr.c | |
28 | +++ linux-2.6.27/fs/ocfs2/xattr.c | |
29 | @@ -2317,9 +2317,12 @@ static int ocfs2_xattr_bucket_find(struc | |
30 | ||
31 | /* | |
32 | * Check whether the hash of the last entry in our | |
33 | - * bucket is larger than the search one. | |
34 | + * bucket is larger than the search one. for an empty | |
35 | + * bucket, the last one is also the first one. | |
36 | */ | |
37 | - xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1]; | |
38 | + if (xh->xh_count) | |
39 | + xe = &xh->xh_entries[le16_to_cpu(xh->xh_count) - 1]; | |
40 | + | |
41 | last_hash = le32_to_cpu(xe->xe_name_hash); | |
42 | ||
43 | /* record lower_bh which may be the insert place. */ | |
44 | @@ -2466,7 +2469,8 @@ static int ocfs2_iterate_xattr_buckets(s | |
45 | if (i == 0) | |
46 | num_buckets = le16_to_cpu(bucket.xh->xh_num_buckets); | |
47 | ||
48 | - mlog(0, "iterating xattr bucket %llu\n", blkno); | |
49 | + mlog(0, "iterating xattr bucket %llu, first hash %u\n", blkno, | |
50 | + le32_to_cpu(bucket.xh->xh_entries[0].xe_name_hash)); | |
51 | if (func) { | |
52 | ret = func(inode, &bucket, para); | |
53 | if (ret) { | |
54 | @@ -3931,8 +3935,6 @@ static inline char *ocfs2_xattr_bucket_g | |
55 | ||
56 | /* | |
57 | * Handle the normal xattr set, including replace, delete and new. | |
58 | - * When the bucket is empty, "is_empty" is set and the caller can | |
59 | - * free this bucket. | |
60 | * | |
61 | * Note: "local" indicates the real data's locality. So we can't | |
62 | * just its bucket locality by its length. | |
63 | @@ -3941,8 +3943,7 @@ static void ocfs2_xattr_set_entry_normal | |
64 | struct ocfs2_xattr_info *xi, | |
65 | struct ocfs2_xattr_search *xs, | |
66 | u32 name_hash, | |
67 | - int local, | |
68 | - int *is_empty) | |
69 | + int local) | |
70 | { | |
71 | struct ocfs2_xattr_entry *last, *xe; | |
72 | int name_len = strlen(xi->name); | |
73 | @@ -3995,14 +3996,23 @@ static void ocfs2_xattr_set_entry_normal | |
74 | ocfs2_xattr_set_local(xe, local); | |
75 | return; | |
76 | } else { | |
77 | - /* Remove the old entry. */ | |
78 | + /* | |
79 | + * Remove the old entry if there is more than one. | |
80 | + * We don't remove the last entry so that we can | |
81 | + * use it to indicate the hash value of the empty | |
82 | + * bucket. | |
83 | + */ | |
84 | last -= 1; | |
85 | - memmove(xe, xe + 1, | |
86 | - (void *)last - (void *)xe); | |
87 | - memset(last, 0, sizeof(struct ocfs2_xattr_entry)); | |
88 | le16_add_cpu(&xh->xh_count, -1); | |
89 | - if (xh->xh_count == 0 && is_empty) | |
90 | - *is_empty = 1; | |
91 | + if (xh->xh_count) { | |
92 | + memmove(xe, xe + 1, | |
93 | + (void *)last - (void *)xe); | |
94 | + memset(last, 0, | |
95 | + sizeof(struct ocfs2_xattr_entry)); | |
96 | + } else | |
97 | + xh->xh_free_start = | |
98 | + cpu_to_le16(OCFS2_XATTR_BUCKET_SIZE); | |
99 | + | |
100 | return; | |
101 | } | |
102 | } else { | |
103 | @@ -4010,7 +4020,7 @@ static void ocfs2_xattr_set_entry_normal | |
104 | int low = 0, high = count - 1, tmp; | |
105 | struct ocfs2_xattr_entry *tmp_xe; | |
106 | ||
107 | - while (low <= high) { | |
108 | + while (low <= high && count) { | |
109 | tmp = (low + high) / 2; | |
110 | tmp_xe = &xh->xh_entries[tmp]; | |
111 | ||
112 | @@ -4106,8 +4116,7 @@ static int ocfs2_xattr_set_entry_in_buck | |
113 | struct ocfs2_xattr_info *xi, | |
114 | struct ocfs2_xattr_search *xs, | |
115 | u32 name_hash, | |
116 | - int local, | |
117 | - int *bucket_empty) | |
118 | + int local) | |
119 | { | |
120 | int i, ret; | |
121 | handle_t *handle = NULL; | |
122 | @@ -4146,8 +4155,7 @@ static int ocfs2_xattr_set_entry_in_buck | |
123 | } | |
124 | } | |
125 | ||
126 | - ocfs2_xattr_set_entry_normal(inode, xi, xs, name_hash, | |
127 | - local, bucket_empty); | |
128 | + ocfs2_xattr_set_entry_normal(inode, xi, xs, name_hash, local); | |
129 | ||
130 | /*Only dirty the blocks we have touched in set xattr. */ | |
131 | ret = ocfs2_xattr_bucket_handle_journal(inode, handle, xs, | |
132 | @@ -4296,69 +4304,6 @@ static int ocfs2_xattr_bucket_set_value_ | |
133 | return __ocfs2_xattr_set_value_outside(inode, xv, val, value_len); | |
134 | } | |
135 | ||
136 | -/* | |
137 | - * Remove the xattr bucket pointed by bucket_bh. | |
138 | - * All the buckets after it in the same xattr extent rec will be | |
139 | - * move forward one by one. | |
140 | - */ | |
141 | -static int ocfs2_rm_xattr_bucket(struct inode *inode, | |
142 | - struct buffer_head *first_bh, | |
143 | - struct ocfs2_xattr_bucket *bucket) | |
144 | -{ | |
145 | - int ret = 0, credits; | |
146 | - struct ocfs2_xattr_header *xh = | |
147 | - (struct ocfs2_xattr_header *)first_bh->b_data; | |
148 | - u16 bucket_num = le16_to_cpu(xh->xh_num_buckets); | |
149 | - u64 end, start = bucket->bhs[0]->b_blocknr; | |
150 | - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | |
151 | - handle_t *handle; | |
152 | - u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); | |
153 | - | |
154 | - end = first_bh->b_blocknr + (bucket_num - 1) * blk_per_bucket; | |
155 | - | |
156 | - mlog(0, "rm xattr bucket %llu\n", start); | |
157 | - /* | |
158 | - * We need to update the first xattr_header and all the buckets starting | |
159 | - * from start in this xattr rec. | |
160 | - * | |
161 | - * XXX: Should we empty the old last bucket here? | |
162 | - */ | |
163 | - credits = 1 + end - start; | |
164 | - handle = ocfs2_start_trans(osb, credits); | |
165 | - if (IS_ERR(handle)) { | |
166 | - ret = PTR_ERR(handle); | |
167 | - mlog_errno(ret); | |
168 | - return ret; | |
169 | - } | |
170 | - | |
171 | - ret = ocfs2_journal_access(handle, inode, first_bh, | |
172 | - OCFS2_JOURNAL_ACCESS_WRITE); | |
173 | - if (ret) { | |
174 | - mlog_errno(ret); | |
175 | - goto out_commit; | |
176 | - } | |
177 | - | |
178 | - | |
179 | - while (start < end) { | |
180 | - ret = ocfs2_cp_xattr_bucket(inode, handle, | |
181 | - start + blk_per_bucket, | |
182 | - start, 0); | |
183 | - if (ret) { | |
184 | - mlog_errno(ret); | |
185 | - goto out_commit; | |
186 | - } | |
187 | - start += blk_per_bucket; | |
188 | - } | |
189 | - | |
190 | - /* update the first_bh. */ | |
191 | - xh->xh_num_buckets = cpu_to_le16(bucket_num - 1); | |
192 | - ocfs2_journal_dirty(handle, first_bh); | |
193 | - | |
194 | -out_commit: | |
195 | - ocfs2_commit_trans(osb, handle); | |
196 | - return ret; | |
197 | -} | |
198 | - | |
199 | static int ocfs2_rm_xattr_cluster(struct inode *inode, | |
200 | struct buffer_head *root_bh, | |
201 | u64 blkno, | |
202 | @@ -4448,57 +4393,6 @@ out: | |
203 | return ret; | |
204 | } | |
205 | ||
206 | -/* | |
207 | - * Free the xattr bucket indicated by xs->bucket and if all the buckets | |
208 | - * in the clusters is free, free the clusters also. | |
209 | - */ | |
210 | -static int ocfs2_xattr_bucket_shrink(struct inode *inode, | |
211 | - struct ocfs2_xattr_info *xi, | |
212 | - struct ocfs2_xattr_search *xs, | |
213 | - u32 name_hash) | |
214 | -{ | |
215 | - int ret; | |
216 | - u32 e_cpos, num_clusters; | |
217 | - u64 p_blkno; | |
218 | - struct buffer_head *first_bh = NULL; | |
219 | - struct ocfs2_xattr_header *first_xh; | |
220 | - struct ocfs2_xattr_block *xb = | |
221 | - (struct ocfs2_xattr_block *)xs->xattr_bh->b_data; | |
222 | - | |
223 | - BUG_ON(xs->header->xh_count != 0); | |
224 | - | |
225 | - ret = ocfs2_xattr_get_rec(inode, name_hash, &p_blkno, | |
226 | - &e_cpos, &num_clusters, | |
227 | - &xb->xb_attrs.xb_root.xt_list); | |
228 | - if (ret) { | |
229 | - mlog_errno(ret); | |
230 | - return ret; | |
231 | - } | |
232 | - | |
233 | - ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), p_blkno, | |
234 | - &first_bh, OCFS2_BH_CACHED, inode); | |
235 | - if (ret) { | |
236 | - mlog_errno(ret); | |
237 | - return ret; | |
238 | - } | |
239 | - | |
240 | - ret = ocfs2_rm_xattr_bucket(inode, first_bh, &xs->bucket); | |
241 | - if (ret) { | |
242 | - mlog_errno(ret); | |
243 | - goto out; | |
244 | - } | |
245 | - | |
246 | - first_xh = (struct ocfs2_xattr_header *)first_bh->b_data; | |
247 | - if (first_xh->xh_num_buckets == 0) | |
248 | - ret = ocfs2_rm_xattr_cluster(inode, xs->xattr_bh, | |
249 | - p_blkno, e_cpos, | |
250 | - num_clusters); | |
251 | - | |
252 | -out: | |
253 | - brelse(first_bh); | |
254 | - return ret; | |
255 | -} | |
256 | - | |
257 | static void ocfs2_xattr_bucket_remove_xs(struct inode *inode, | |
258 | struct ocfs2_xattr_search *xs) | |
259 | { | |
260 | @@ -4550,7 +4444,7 @@ static int ocfs2_xattr_set_in_bucket(str | |
261 | struct ocfs2_xattr_info *xi, | |
262 | struct ocfs2_xattr_search *xs) | |
263 | { | |
264 | - int ret, local = 1, bucket_empty = 0; | |
265 | + int ret, local = 1; | |
266 | size_t value_len; | |
267 | char *val = (char *)xi->value; | |
268 | struct ocfs2_xattr_entry *xe = xs->here; | |
269 | @@ -4596,34 +4490,29 @@ static int ocfs2_xattr_set_in_bucket(str | |
270 | xi->value_len = OCFS2_XATTR_ROOT_SIZE; | |
271 | } | |
272 | ||
273 | - ret = ocfs2_xattr_set_entry_in_bucket(inode, xi, xs, name_hash, | |
274 | - local, &bucket_empty); | |
275 | + ret = ocfs2_xattr_set_entry_in_bucket(inode, xi, xs, name_hash, local); | |
276 | if (ret) { | |
277 | mlog_errno(ret); | |
278 | goto out; | |
279 | } | |
280 | ||
281 | - if (value_len > OCFS2_XATTR_INLINE_SIZE) { | |
282 | - /* allocate the space now for the outside block storage. */ | |
283 | - ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs, | |
284 | - value_len); | |
285 | - if (ret) { | |
286 | - mlog_errno(ret); | |
287 | + if (value_len <= OCFS2_XATTR_INLINE_SIZE) | |
288 | + goto out; | |
289 | ||
290 | - if (xs->not_found) { | |
291 | - /* | |
292 | - * We can't allocate enough clusters for outside | |
293 | - * storage and we have allocated xattr already, | |
294 | - * so need to remove it. | |
295 | - */ | |
296 | - ocfs2_xattr_bucket_remove_xs(inode, xs); | |
297 | - } | |
298 | - goto out; | |
299 | + /* allocate the space now for the outside block storage. */ | |
300 | + ret = ocfs2_xattr_bucket_value_truncate_xs(inode, xs, | |
301 | + value_len); | |
302 | + if (ret) { | |
303 | + mlog_errno(ret); | |
304 | + | |
305 | + if (xs->not_found) { | |
306 | + /* | |
307 | + * We can't allocate enough clusters for outside | |
308 | + * storage and we have allocated xattr already, | |
309 | + * so need to remove it. | |
310 | + */ | |
311 | + ocfs2_xattr_bucket_remove_xs(inode, xs); | |
312 | } | |
313 | - } else { | |
314 | - if (bucket_empty) | |
315 | - ret = ocfs2_xattr_bucket_shrink(inode, xi, | |
316 | - xs, name_hash); | |
317 | goto out; | |
318 | } | |
319 |