X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=ldiskfs%2Fkernel_patches%2Fpatches%2Frhel6.3%2Fext4-large-eas.patch;h=a321b21c9f2be9e4ff41054697d3a429a5fcec8a;hp=0fc411ab77b4e6e693d9f1624d474f5b95cab9b6;hb=945fd61b80f22a4148c4c0953ddc4dfcd75337de;hpb=574a82bd10e27c8246e66a2127b16b7d5e9d8697 diff --git a/ldiskfs/kernel_patches/patches/rhel6.3/ext4-large-eas.patch b/ldiskfs/kernel_patches/patches/rhel6.3/ext4-large-eas.patch index 0fc411a..a321b21 100644 --- a/ldiskfs/kernel_patches/patches/rhel6.3/ext4-large-eas.patch +++ b/ldiskfs/kernel_patches/patches/rhel6.3/ext4-large-eas.patch @@ -2,7 +2,7 @@ Index: linux-stage/fs/ext4/ext4.h =================================================================== --- linux-stage.orig/fs/ext4/ext4.h +++ linux-stage/fs/ext4/ext4.h -@@ -1267,6 +1267,7 @@ EXT4_INODE_BIT_FNS(state, state_flags) +@@ -1329,6 +1329,7 @@ EXT4_INODE_BIT_FNS(state, state_flags) #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 @@ -10,7 +10,7 @@ Index: linux-stage/fs/ext4/ext4.h #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 #define EXT4_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR -@@ -1276,6 +1277,7 @@ EXT4_INODE_BIT_FNS(state, state_flags) +@@ -1338,6 +1339,7 @@ EXT4_INODE_BIT_FNS(state, state_flags) EXT4_FEATURE_INCOMPAT_EXTENTS| \ EXT4_FEATURE_INCOMPAT_64BIT| \ EXT4_FEATURE_INCOMPAT_FLEX_BG| \ @@ -18,8 +18,8 @@ Index: linux-stage/fs/ext4/ext4.h EXT4_FEATURE_INCOMPAT_MMP| \ EXT4_FEATURE_INCOMPAT_DIRDATA) -@@ -1607,6 +1609,12 @@ struct mmpd_data { - #endif +@@ -1695,6 +1697,12 @@ struct mmpd_data { + #define EXT4_MMP_MAX_CHECK_INTERVAL 300UL /* + * Maximum size of xattr attributes for FEATURE_INCOMPAT_EA_INODE 1Mb @@ -31,6 +31,17 @@ Index: linux-stage/fs/ext4/ext4.h * Function prototypes */ +@@ -1706,6 +1714,10 @@ struct mmpd_data { + # define ATTRIB_NORET __attribute__((noreturn)) + # define NORET_AND noreturn, + ++struct ext4_xattr_ino_array { ++ unsigned int xia_count; /* # of used item in the array */ ++ unsigned int xia_inodes[0]; ++}; + /* bitmap.c */ + extern unsigned int ext4_count_free(struct buffer_head *, unsigned); + Index: linux-stage/fs/ext4/xattr.c =================================================================== --- linux-stage.orig/fs/ext4/xattr.c @@ -46,11 +57,10 @@ Index: linux-stage/fs/ext4/xattr.c size_t value_size = le32_to_cpu(entry->e_value_size); - if (entry->e_value_block != 0 || value_size > size || -- le16_to_cpu(entry->e_value_offs) + value_size > size) -+ if ((entry->e_value_inum == 0) && -+ (le16_to_cpu(entry->e_value_offs) + value_size > size)) ++ if (!entry->e_value_inum && + le16_to_cpu(entry->e_value_offs) + value_size > size) + return -EIO; -+ if (entry->e_value_inum != 0 && ++ if (entry->e_value_inum && + (le32_to_cpu(entry->e_value_inum) < EXT4_FIRST_INO(inode->i_sb) || + le32_to_cpu(entry->e_value_inum) > + le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_inodes_count))) @@ -66,7 +76,7 @@ Index: linux-stage/fs/ext4/xattr.c { struct ext4_xattr_entry *entry; size_t name_len; -@@ -200,11 +207,103 @@ ext4_xattr_find_entry(struct ext4_xattr_ +@@ -200,11 +207,104 @@ ext4_xattr_find_entry(struct ext4_xattr_ break; } *pentry = entry; @@ -113,28 +123,29 @@ Index: linux-stage/fs/ext4/xattr.c + return err; +} + -+struct inode *ext4_xattr_inode_iget(struct inode *parent, int ea_ino, int *err) ++struct inode *ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, int *err) +{ + struct inode *ea_inode = NULL; + + ea_inode = ext4_iget(parent->i_sb, ea_ino); -+ if (ea_inode == NULL || is_bad_inode(ea_inode)) { -+ ext4_error(parent->i_sb, "error while reading EA inode %d", -+ ea_ino); -+ *err = -EIO; ++ if (IS_ERR(ea_inode) || is_bad_inode(ea_inode)) { ++ int rc = IS_ERR(ea_inode) ? PTR_ERR(ea_inode) : 0; ++ ext4_error(parent->i_sb, "error while reading EA inode %lu " ++ "/ %d %d", ea_ino, rc, is_bad_inode(ea_inode)); ++ *err = rc != 0 ? rc : -EIO; + return NULL; + } + -+ if (ea_inode->i_xattr_inode_parent != parent->i_ino || ++ if (EXT4_XATTR_INODE_GET_PARENT(ea_inode) != parent->i_ino || + ea_inode->i_generation != parent->i_generation) { -+ ext4_error(parent->i_sb, "Backpointer from EA inode %d " ++ ext4_error(parent->i_sb, "Backpointer from EA inode %lu " + "to parent invalid.", ea_ino); + *err = -EINVAL; + goto error; + } + + if (!(EXT4_I(ea_inode)->i_flags & EXT4_EA_INODE_FL)) { -+ ext4_error(parent->i_sb, "EA inode %d does not have " ++ ext4_error(parent->i_sb, "EA inode %lu does not have " + "EXT4_EA_INODE_FL flag set.\n", ea_ino); + *err = -EINVAL; + goto error; @@ -152,7 +163,7 @@ Index: linux-stage/fs/ext4/xattr.c + * Read the value from the EA inode. + */ +static int -+ext4_xattr_inode_get(struct inode *inode, int ea_ino, void *buffer, ++ext4_xattr_inode_get(struct inode *inode, unsigned long ea_ino, void *buffer, + size_t *size) +{ + struct inode *ea_inode = NULL; @@ -187,7 +198,7 @@ Index: linux-stage/fs/ext4/xattr.c goto cleanup; - memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs), - size); -+ if (entry->e_value_inum != 0) { ++ if (entry->e_value_inum) { + error = ext4_xattr_inode_get(inode, + le32_to_cpu(entry->e_value_inum), + buffer, &size); @@ -215,7 +226,7 @@ Index: linux-stage/fs/ext4/xattr.c goto cleanup; - memcpy(buffer, (void *)IFIRST(header) + - le16_to_cpu(entry->e_value_offs), size); -+ if (entry->e_value_inum != 0) { ++ if (entry->e_value_inum) { + error = ext4_xattr_inode_get(inode, + le32_to_cpu(entry->e_value_inum), + buffer, &size); @@ -228,16 +239,16 @@ Index: linux-stage/fs/ext4/xattr.c } error = size; -@@ -512,7 +628,7 @@ static size_t ext4_xattr_free_space(stru +@@ -513,7 +629,7 @@ static size_t ext4_xattr_free_space(stru { for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { *total += EXT4_XATTR_LEN(last->e_name_len); - if (!last->e_value_block && last->e_value_size) { -+ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ 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; -@@ -521,11 +637,159 @@ static size_t ext4_xattr_free_space(stru +@@ -522,11 +638,159 @@ static size_t ext4_xattr_free_space(stru return (*min_offs - ((void *)last - base) - sizeof(__u32)); } @@ -292,7 +303,7 @@ Index: linux-stage/fs/ext4/xattr.c + + memcpy(bh->b_data, buf, csize); + set_buffer_uptodate(bh); -+ ext4_journal_dirty_metadata(handle, bh); ++ ext4_handle_dirty_metadata(handle, ea_inode, bh); + + buf += csize; + wsize += csize; @@ -336,7 +347,7 @@ Index: linux-stage/fs/ext4/xattr.c + * A back-pointer from EA inode to parent inode will be useful + * for e2fsck. + */ -+ ea_inode->i_xattr_inode_parent = inode->i_ino; ++ EXT4_XATTR_INODE_SET_PARENT(ea_inode, inode->i_ino); + unlock_new_inode(ea_inode); + } + @@ -346,8 +357,8 @@ Index: linux-stage/fs/ext4/xattr.c +/* + * Unlink the inode storing the value of the EA. + */ -+static int -+ext4_xattr_inode_unlink(struct inode *inode, int ea_ino) ++int ++ext4_xattr_inode_unlink(struct inode *inode, unsigned long ea_ino) +{ + struct inode *ea_inode = NULL; + int err; @@ -366,7 +377,7 @@ Index: linux-stage/fs/ext4/xattr.c + * Add value of the EA in an inode. + */ +static int -+ext4_xattr_inode_set(handle_t *handle, struct inode *inode, int *ea_ino, ++ext4_xattr_inode_set(handle_t *handle, struct inode *inode, unsigned long *ea_ino, + const void *value, size_t value_len) +{ + struct inode *ea_inode = NULL; @@ -398,7 +409,7 @@ Index: linux-stage/fs/ext4/xattr.c }; struct ext4_xattr_search { -@@ -537,15 +801,23 @@ struct ext4_xattr_search { +@@ -538,15 +802,23 @@ struct ext4_xattr_search { }; static int @@ -420,17 +431,17 @@ Index: linux-stage/fs/ext4/xattr.c last = s->first; for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { - if (!last->e_value_block && last->e_value_size) { -+ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ 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; -@@ -553,16 +825,21 @@ ext4_xattr_set_entry(struct ext4_xattr_i +@@ -554,16 +826,21 @@ ext4_xattr_set_entry(struct ext4_xattr_i } free = min_offs - ((void *)last - s->base) - sizeof(__u32); if (!s->not_found) { - if (!s->here->e_value_block && s->here->e_value_size) { -+ if (!in_inode && s->here->e_value_inum == 0 && -+ s->here->e_value_size > 0) { ++ if (!in_inode && ++ !s->here->e_value_inum && s->here->e_value_size) { size_t size = le32_to_cpu(s->here->e_value_size); free += EXT4_XATTR_SIZE(size); } @@ -450,29 +461,29 @@ Index: linux-stage/fs/ext4/xattr.c return -ENOSPC; } -@@ -576,7 +853,8 @@ ext4_xattr_set_entry(struct ext4_xattr_i +@@ -577,7 +854,8 @@ ext4_xattr_set_entry(struct ext4_xattr_i s->here->e_name_len = name_len; memcpy(s->here->e_name, i->name, name_len); } else { - if (!s->here->e_value_block && s->here->e_value_size) { -+ if (s->here->e_value_offs > 0 && s->here->e_value_inum == 0 && -+ s->here->e_value_size > 0) { ++ if (!s->here->e_value_inum && s->here->e_value_size && ++ s->here->e_value_offs > 0) { void *first_val = s->base + min_offs; size_t offs = le16_to_cpu(s->here->e_value_offs); void *val = s->base + offs; -@@ -605,13 +883,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i +@@ -606,13 +884,18 @@ ext4_xattr_set_entry(struct ext4_xattr_i last = s->first; while (!IS_LAST_ENTRY(last)) { size_t o = le16_to_cpu(last->e_value_offs); - if (!last->e_value_block && -- last->e_value_size && o < offs) -+ if (last->e_value_size > 0 && o < offs) ++ if (!last->e_value_inum && + last->e_value_size && o < offs) last->e_value_offs = cpu_to_le16(o + size); last = EXT4_XATTR_NEXT(last); } } -+ if (s->here->e_value_inum != 0) { ++ if (s->here->e_value_inum) { + ext4_xattr_inode_unlink(inode, + le32_to_cpu(s->here->e_value_inum)); + s->here->e_value_inum = 0; @@ -480,13 +491,13 @@ Index: linux-stage/fs/ext4/xattr.c if (!i->value) { /* Remove the old name. */ size_t size = EXT4_XATTR_LEN(name_len); -@@ -625,10 +907,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i +@@ -626,10 +908,17 @@ ext4_xattr_set_entry(struct ext4_xattr_i if (i->value) { /* Insert the new value. */ s->here->e_value_size = cpu_to_le32(i->value_len); - if (i->value_len) { + if (in_inode) { -+ int ea_ino = le32_to_cpu(s->here->e_value_inum); ++ unsigned long ea_ino = le32_to_cpu(s->here->e_value_inum); + ext4_xattr_inode_set(handle, inode, &ea_ino, i->value, + i->value_len); + s->here->e_value_inum = cpu_to_le32(ea_ino); @@ -499,7 +510,7 @@ Index: linux-stage/fs/ext4/xattr.c memset(val + size - EXT4_XATTR_PAD, 0, EXT4_XATTR_PAD); /* Clear the pad bytes. */ memcpy(val, i->value, i->value_len); -@@ -673,7 +962,7 @@ ext4_xattr_block_find(struct inode *inod +@@ -674,7 +963,7 @@ ext4_xattr_block_find(struct inode *inod bs->s.end = bs->bh->b_data + bs->bh->b_size; bs->s.here = bs->s.first; error = ext4_xattr_find_entry(&bs->s.here, i->name_index, @@ -508,7 +519,7 @@ Index: linux-stage/fs/ext4/xattr.c if (error && error != -ENODATA) goto cleanup; bs->s.not_found = error; -@@ -697,8 +986,6 @@ ext4_xattr_block_set(handle_t *handle, s +@@ -698,8 +987,6 @@ ext4_xattr_block_set(handle_t *handle, s #define header(x) ((struct ext4_xattr_header *)(x)) @@ -517,7 +528,7 @@ Index: linux-stage/fs/ext4/xattr.c if (s->base) { ce = mb_cache_entry_get(ext4_xattr_cache, bs->bh->b_bdev, bs->bh->b_blocknr); -@@ -713,7 +1000,7 @@ ext4_xattr_block_set(handle_t *handle, s +@@ -714,7 +1001,7 @@ ext4_xattr_block_set(handle_t *handle, s ce = NULL; } ea_bdebug(bs->bh, "modifying in-place"); @@ -526,7 +537,7 @@ Index: linux-stage/fs/ext4/xattr.c if (!error) { if (!IS_LAST_ENTRY(s->first)) ext4_xattr_rehash(header(s->base), -@@ -765,7 +1052,7 @@ ext4_xattr_block_set(handle_t *handle, s +@@ -766,7 +1053,7 @@ ext4_xattr_block_set(handle_t *handle, s s->end = s->base + sb->s_blocksize; } @@ -535,7 +546,7 @@ Index: linux-stage/fs/ext4/xattr.c if (error == -EIO) goto bad_block; if (error) -@@ -909,7 +1196,7 @@ ext4_xattr_ibody_find(struct inode *inod +@@ -917,7 +1204,7 @@ ext4_xattr_ibody_find(struct inode *inod /* Find the named attribute. */ error = ext4_xattr_find_entry(&is->s.here, i->name_index, i->name, is->s.end - @@ -544,7 +555,7 @@ Index: linux-stage/fs/ext4/xattr.c if (error && error != -ENODATA) return error; is->s.not_found = error; -@@ -928,7 +1215,7 @@ ext4_xattr_ibody_set(handle_t *handle, s +@@ -936,7 +1223,7 @@ ext4_xattr_ibody_set(handle_t *handle, s if (EXT4_I(inode)->i_extra_isize == 0) return -ENOSPC; @@ -553,7 +564,7 @@ Index: linux-stage/fs/ext4/xattr.c if (error) return error; header = IHDR(inode, ext4_raw_inode(&is->iloc)); -@@ -964,7 +1251,7 @@ ext4_xattr_set_handle(handle_t *handle, +@@ -972,7 +1259,7 @@ ext4_xattr_set_handle(handle_t *handle, .name = name, .value = value, .value_len = value_len, @@ -562,7 +573,7 @@ Index: linux-stage/fs/ext4/xattr.c }; struct ext4_xattr_ibody_find is = { .s = { .not_found = -ENODATA, }, -@@ -1033,6 +1320,15 @@ ext4_xattr_set_handle(handle_t *handle, +@@ -1041,6 +1328,15 @@ ext4_xattr_set_handle(handle_t *handle, goto cleanup; } error = ext4_xattr_block_set(handle, inode, &i, &bs); @@ -578,7 +589,7 @@ Index: linux-stage/fs/ext4/xattr.c if (error) goto cleanup; if (!is.s.not_found) { -@@ -1080,10 +1376,25 @@ ext4_xattr_set(struct inode *inode, int +@@ -1087,10 +1383,25 @@ ext4_xattr_set(struct inode *inode, int const void *value, size_t value_len, int flags) { handle_t *handle; @@ -605,7 +616,7 @@ Index: linux-stage/fs/ext4/xattr.c if (IS_ERR(handle)) { error = PTR_ERR(handle); } else { -@@ -1093,7 +1404,7 @@ retry: +@@ -1100,7 +1411,7 @@ retry: value, value_len, flags); error2 = ext4_journal_stop(handle); if (error == -ENOSPC && @@ -614,16 +625,99 @@ Index: linux-stage/fs/ext4/xattr.c goto retry; if (error == 0) error = error2; -@@ -1115,7 +1426,7 @@ static void ext4_xattr_shift_entries(str +@@ -1122,7 +1433,7 @@ static void ext4_xattr_shift_entries(str /* Adjust the value offsets of the entries */ for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { - if (!last->e_value_block && last->e_value_size) { -+ if (last->e_value_inum == 0 && last->e_value_size > 0) { ++ if (!last->e_value_inum && last->e_value_size) { new_offs = le16_to_cpu(last->e_value_offs) + value_offs_shift; BUG_ON(new_offs + le32_to_cpu(last->e_value_size) -@@ -1353,15 +1664,41 @@ cleanup: +@@ -1355,22 +1666,135 @@ cleanup: + return error; + } + ++#define EIA_INCR 16 /* must be 2^n */ ++#define EIA_MASK (EIA_INCR - 1) ++/* Add the large xattr @ino into @lea_ino_array for later deletion. ++ * If @lea_ino_array is new or full it will be grown and the old ++ * contents copied over. ++ */ ++static int ++ext4_expand_ino_array(struct ext4_xattr_ino_array **lea_ino_array, __u32 ino) ++{ ++ if (*lea_ino_array == NULL) { ++ /* ++ * Start with 15 inodes, so it fits into a power-of-two size. ++ * If *lea_ino_array is NULL, this is essentially offsetof() ++ */ ++ (*lea_ino_array) = ++ kmalloc(offsetof(struct ext4_xattr_ino_array, ++ xia_inodes[EIA_MASK]), ++ GFP_NOFS); ++ if (*lea_ino_array == NULL) ++ return -ENOMEM; ++ (*lea_ino_array)->xia_count = 0; ++ } else if (((*lea_ino_array)->xia_count & EIA_MASK) == EIA_MASK) { ++ /* expand the array once all 15 + n * 16 slots are full */ ++ struct ext4_xattr_ino_array *new_array = NULL; ++ int count = (*lea_ino_array)->xia_count; ++ ++ /* if new_array is NULL, this is essentially offsetof() */ ++ new_array = kmalloc( ++ offsetof(struct ext4_xattr_ino_array, ++ xia_inodes[count + EIA_INCR]), ++ GFP_NOFS); ++ if (new_array == NULL) ++ return -ENOMEM; ++ memcpy(new_array, *lea_ino_array, ++ offsetof(struct ext4_xattr_ino_array, ++ xia_inodes[count])); ++ kfree(*lea_ino_array); ++ *lea_ino_array = new_array; ++ } ++ (*lea_ino_array)->xia_inodes[(*lea_ino_array)->xia_count++] = ino; ++ return 0; ++} + ++/** ++ * Add xattr inode to orphan list ++ */ ++static int ++ext4_xattr_inode_orphan_add(handle_t *handle, struct inode *inode, ++ int credits, struct ext4_xattr_ino_array *lea_ino_array) ++{ ++ struct inode *ea_inode = NULL; ++ int idx = 0, error = 0; ++ ++ if (lea_ino_array == NULL) ++ return 0; ++ ++ for (; idx < lea_ino_array->xia_count; ++idx) { ++ if (!ext4_handle_has_enough_credits(handle, credits)) { ++ error = ext4_journal_extend(handle, credits); ++ if (error > 0) ++ error = ext4_journal_restart(handle, credits); ++ ++ if (error != 0) { ++ ext4_warning(inode->i_sb, ++ "couldn't extend journal " ++ "(err %d)", error); ++ return error; ++ } ++ } ++ ea_inode = ext4_xattr_inode_iget(inode, ++ lea_ino_array->xia_inodes[idx], &error); ++ if (error) ++ continue; ++ ext4_orphan_add(handle, ea_inode); ++ /* the inode's i_count will be released by caller */ ++ } ++ ++ return 0; ++} + /* * ext4_xattr_delete_inode() * @@ -636,16 +730,20 @@ Index: linux-stage/fs/ext4/xattr.c + * xattr block and all xattr inodes. They are checked by ext4_xattr_inode_iget() + * to ensure they belong to the parent inode and were not deleted already. */ - void - ext4_xattr_delete_inode(handle_t *handle, struct inode *inode) +-void +-ext4_xattr_delete_inode(handle_t *handle, struct inode *inode) ++int ++ext4_xattr_delete_inode(handle_t *handle, struct inode *inode, ++ struct ext4_xattr_ino_array **lea_ino_array) { struct buffer_head *bh = NULL; + struct ext4_xattr_ibody_header *header; + struct ext4_inode *raw_inode; + struct ext4_iloc iloc; + struct ext4_xattr_entry *entry; -+ int error; -+ ++ int credits = 3, error = 0; + +- if (!EXT4_I(inode)->i_file_acl) + if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR)) + goto delete_external_ea; + @@ -654,37 +752,102 @@ Index: linux-stage/fs/ext4/xattr.c + goto cleanup; + raw_inode = ext4_raw_inode(&iloc); + header = IHDR(inode, raw_inode); -+ entry = IFIRST(header); -+ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { -+ if (entry->e_value_inum != 0) { -+ ext4_xattr_inode_unlink(inode, -+ le32_to_cpu(entry->e_value_inum)); -+ entry->e_value_inum = 0; ++ for (entry = IFIRST(header); !IS_LAST_ENTRY(entry); ++ entry = EXT4_XATTR_NEXT(entry)) { ++ if (!entry->e_value_inum) ++ continue; ++ if (ext4_expand_ino_array(lea_ino_array, ++ entry->e_value_inum) != 0) { ++ brelse(iloc.bh); ++ goto cleanup; + } ++ entry->e_value_inum = 0; + } - ++ brelse(iloc.bh); ++ +delete_external_ea: - if (!EXT4_I(inode)->i_file_acl) ++ if (!EXT4_I(inode)->i_file_acl) { ++ /* add xattr inode to orphan list */ ++ ext4_xattr_inode_orphan_add(handle, inode, credits, ++ *lea_ino_array); goto cleanup; ++ } bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); -@@ -1376,6 +1713,16 @@ ext4_xattr_delete_inode(handle_t *handle + if (!bh) { + ext4_error(inode->i_sb, "inode %lu: block %llu read error", +@@ -1383,11 +1807,71 @@ ext4_xattr_delete_inode(handle_t *handle inode->i_ino, EXT4_I(inode)->i_file_acl); goto cleanup; } + -+ entry = BFIRST(bh); -+ for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { -+ if (entry->e_value_inum != 0) { -+ ext4_xattr_inode_unlink(inode, -+ le32_to_cpu(entry->e_value_inum)); -+ entry->e_value_inum = 0; ++ for (entry = BFIRST(bh); !IS_LAST_ENTRY(entry); ++ entry = EXT4_XATTR_NEXT(entry)) { ++ if (!entry->e_value_inum) ++ continue; ++ if (ext4_expand_ino_array(lea_ino_array, ++ entry->e_value_inum) != 0) ++ goto cleanup; ++ entry->e_value_inum = 0; ++ } ++ ++ /* add xattr inode to orphan list */ ++ error = ext4_xattr_inode_orphan_add(handle, inode, credits, ++ *lea_ino_array); ++ if (error != 0) ++ goto cleanup; ++ ++ if (!IS_NOQUOTA(inode)) ++ credits += 2 * EXT4_QUOTA_DEL_BLOCKS(inode->i_sb); ++ ++ if (!ext4_handle_has_enough_credits(handle, credits)) { ++ error = ext4_journal_extend(handle, credits); ++ if (error > 0) ++ error = ext4_journal_restart(handle, credits); ++ if (error != 0) { ++ ext4_warning(inode->i_sb, ++ "couldn't extend journal (err %d)", error); ++ goto cleanup; + } + } + ext4_xattr_release_block(handle, inode, bh); EXT4_I(inode)->i_file_acl = 0; -@@ -1450,10 +1797,9 @@ ext4_xattr_cmp(struct ext4_xattr_header + cleanup: + brelse(bh); ++ ++ return error; ++} ++ ++void ++ext4_xattr_inode_array_free(struct inode *inode, ++ struct ext4_xattr_ino_array *lea_ino_array) ++{ ++ struct inode *ea_inode = NULL; ++ int idx = 0; ++ int err; ++ ++ if (lea_ino_array == NULL) ++ return; ++ ++ for (; idx < lea_ino_array->xia_count; ++idx) { ++ ea_inode = ext4_xattr_inode_iget(inode, ++ lea_ino_array->xia_inodes[idx], &err); ++ if (err) ++ continue; ++ ++ /* for inode's i_count get from ext4_xattr_delete_inode */ ++ if (!list_empty(&EXT4_I(ea_inode)->i_orphan)) ++ iput(ea_inode); ++ ++ ea_inode->i_nlink = 0; ++ iput(ea_inode); ++ } ++ kfree(lea_ino_array); + } + + /* +@@ -1457,10 +1941,9 @@ ext4_xattr_cmp(struct ext4_xattr_header entry1->e_name_index != entry2->e_name_index || entry1->e_name_len != entry2->e_name_len || entry1->e_value_size != entry2->e_value_size || @@ -696,12 +859,12 @@ Index: linux-stage/fs/ext4/xattr.c if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs), (char *)header2 + le16_to_cpu(entry2->e_value_offs), le32_to_cpu(entry1->e_value_size))) -@@ -1538,7 +1884,7 @@ static inline void ext4_xattr_hash_entry +@@ -1545,7 +2028,7 @@ static inline void ext4_xattr_hash_entry *name++; } - if (entry->e_value_block == 0 && entry->e_value_size != 0) { -+ if (entry->e_value_inum == 0 && entry->e_value_size != 0) { ++ if (!entry->e_value_inum && entry->e_value_size) { __le32 *value = (__le32 *)((char *)header + le16_to_cpu(entry->e_value_offs)); for (n = (le32_to_cpu(entry->e_value_size) + @@ -718,11 +881,22 @@ Index: linux-stage/fs/ext4/xattr.h __le32 e_value_size; /* size of attribute value */ __le32 e_hash; /* hash value of name and value */ char e_name[0]; /* attribute name */ -@@ -63,6 +63,15 @@ struct ext4_xattr_entry { +@@ -63,6 +63,26 @@ struct ext4_xattr_entry { EXT4_I(inode)->i_extra_isize)) #define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) -+#define i_xattr_inode_parent i_mtime.tv_sec ++/* ++ * Link EA inode back to parent one using i_mtime field. ++ * Extra integer type conversion added to ignore higher ++ * bits in i_mtime.tv_sec which might be set by ext4_get() ++ */ ++#define EXT4_XATTR_INODE_SET_PARENT(inode, inum) \ ++do { \ ++ (inode)->i_mtime.tv_sec = inum; \ ++} while(0) ++ ++#define EXT4_XATTR_INODE_GET_PARENT(inode) \ ++((__u32)(inode)->i_mtime.tv_sec) + +/* + * The minimum size of EA value when you start storing it in an external inode @@ -734,3 +908,132 @@ Index: linux-stage/fs/ext4/xattr.h # ifdef CONFIG_EXT4_FS_XATTR extern struct xattr_handler ext4_xattr_user_handler; +@@ -77,7 +86,13 @@ extern int ext4_xattr_get(struct inode * + extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int); + extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int); + +-extern void ext4_xattr_delete_inode(handle_t *, struct inode *); ++extern struct inode *ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino, ++ int *err); ++extern int ext4_xattr_inode_unlink(struct inode *inode, unsigned long ea_ino); ++extern int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode, ++ struct ext4_xattr_ino_array **array); ++extern void ext4_xattr_inode_array_free(struct inode *inode, ++ struct ext4_xattr_ino_array *array); + extern void ext4_xattr_put_super(struct super_block *); + + extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, +@@ -111,9 +126,11 @@ ext4_xattr_set_handle(handle_t *handle, + return -EOPNOTSUPP; + } + +-static inline void +-ext4_xattr_delete_inode(handle_t *handle, struct inode *inode) ++inline int ++ext4_xattr_delete_inode(handle_t *handle, struct inode *inode, ++ struct ext4_xattr_ino_array **array) + { ++ return -EOPNOTSUPP; + } + + static inline void +Index: linux-stage/fs/ext4/inode.c +=================================================================== +--- linux-stage.orig/fs/ext4/inode.c ++++ linux-stage/fs/ext4/inode.c +@@ -222,6 +222,8 @@ void ext4_delete_inode(struct inode *ino + { + handle_t *handle; + int err; ++ int extra_credits = 3; ++ struct ext4_xattr_ino_array *lea_ino_array = NULL; + + if (ext4_should_order_data(inode)) + ext4_begin_ordered_truncate(inode, 0); +@@ -235,7 +237,8 @@ void ext4_delete_inode(struct inode *ino + * protection against it + */ + sb_start_intwrite(inode->i_sb); +- handle = ext4_journal_start(inode, blocks_for_truncate(inode)+3); ++ ++ handle = ext4_journal_start(inode, extra_credits); + if (IS_ERR(handle)) { + ext4_std_error(inode->i_sb, PTR_ERR(handle)); + /* +@@ -247,9 +250,36 @@ void ext4_delete_inode(struct inode *ino + sb_end_intwrite(inode->i_sb); + goto no_delete; + } +- + if (IS_SYNC(inode)) + ext4_handle_sync(handle); ++ ++ /* ++ * Delete xattr inode before deleting the main inode. ++ */ ++ err = ext4_xattr_delete_inode(handle, inode, &lea_ino_array); ++ if (err) { ++ ext4_warning(inode->i_sb, ++ "couldn't delete inode's xattr (err %d)", err); ++ goto stop_handle; ++ } ++ ++ if (!IS_NOQUOTA(inode)) ++ extra_credits += 2 * EXT4_QUOTA_DEL_BLOCKS(inode->i_sb); ++ ++ if (!ext4_handle_has_enough_credits(handle, ++ blocks_for_truncate(inode) + extra_credits)) { ++ err = ext4_journal_extend(handle, ++ blocks_for_truncate(inode) + extra_credits); ++ if (err > 0) ++ err = ext4_journal_restart(handle, ++ blocks_for_truncate(inode) + extra_credits); ++ if (err != 0) { ++ ext4_warning(inode->i_sb, ++ "couldn't extend journal (err %d)", err); ++ goto stop_handle; ++ } ++ } ++ + inode->i_size = 0; + err = ext4_mark_inode_dirty(handle, inode); + if (err) { +@@ -266,10 +296,10 @@ void ext4_delete_inode(struct inode *ino + * enough credits left in the handle to remove the inode from + * the orphan list and set the dtime field. + */ +- if (!ext4_handle_has_enough_credits(handle, 3)) { +- err = ext4_journal_extend(handle, 3); ++ if (!ext4_handle_has_enough_credits(handle, extra_credits)) { ++ err = ext4_journal_extend(handle, extra_credits); + if (err > 0) +- err = ext4_journal_restart(handle, 3); ++ err = ext4_journal_restart(handle, extra_credits); + if (err != 0) { + ext4_warning(inode->i_sb, + "couldn't extend journal (err %d)", err); +@@ -303,8 +333,12 @@ void ext4_delete_inode(struct inode *ino + clear_inode(inode); + else + ext4_free_inode(handle, inode); ++ + ext4_journal_stop(handle); + sb_end_intwrite(inode->i_sb); ++ ++ if (lea_ino_array != NULL) ++ ext4_xattr_inode_array_free(inode, lea_ino_array); + return; + no_delete: + clear_inode(inode); /* We must guarantee clearing of inode... */ +Index: linux-stage/fs/ext4/ialloc.c +=================================================================== +--- linux-stage.orig/fs/ext4/ialloc.c ++++ linux-stage/fs/ext4/ialloc.c +@@ -219,7 +219,6 @@ void ext4_free_inode(handle_t *handle, s + * as writing the quota to disk may need the lock as well. + */ + vfs_dq_init(inode); +- ext4_xattr_delete_inode(handle, inode); + vfs_dq_free_inode(inode); + vfs_dq_drop(inode); +