Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-microblaze.git] / fs / ext4 / xattr.c
index 564e28a..533216e 100644 (file)
@@ -436,6 +436,21 @@ error:
        return err;
 }
 
+/* Remove entry from mbcache when EA inode is getting evicted */
+void ext4_evict_ea_inode(struct inode *inode)
+{
+       struct mb_cache_entry *oe;
+
+       if (!EA_INODE_CACHE(inode))
+               return;
+       /* Wait for entry to get unused so that we can remove it */
+       while ((oe = mb_cache_entry_delete_or_get(EA_INODE_CACHE(inode),
+                       ext4_xattr_inode_get_hash(inode), inode->i_ino))) {
+               mb_cache_entry_wait_unused(oe);
+               mb_cache_entry_put(EA_INODE_CACHE(inode), oe);
+       }
+}
+
 static int
 ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
                               struct ext4_xattr_entry *entry, void *buffer,
@@ -976,10 +991,8 @@ int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode,
 static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
                                       int ref_change)
 {
-       struct mb_cache *ea_inode_cache = EA_INODE_CACHE(ea_inode);
        struct ext4_iloc iloc;
        s64 ref_count;
-       u32 hash;
        int ret;
 
        inode_lock(ea_inode);
@@ -1002,14 +1015,6 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
 
                        set_nlink(ea_inode, 1);
                        ext4_orphan_del(handle, ea_inode);
-
-                       if (ea_inode_cache) {
-                               hash = ext4_xattr_inode_get_hash(ea_inode);
-                               mb_cache_entry_create(ea_inode_cache,
-                                                     GFP_NOFS, hash,
-                                                     ea_inode->i_ino,
-                                                     true /* reusable */);
-                       }
                }
        } else {
                WARN_ONCE(ref_count < 0, "EA inode %lu ref_count=%lld",
@@ -1022,12 +1027,6 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode,
 
                        clear_nlink(ea_inode);
                        ext4_orphan_add(handle, ea_inode);
-
-                       if (ea_inode_cache) {
-                               hash = ext4_xattr_inode_get_hash(ea_inode);
-                               mb_cache_entry_delete(ea_inode_cache, hash,
-                                                     ea_inode->i_ino);
-                       }
                }
        }
 
@@ -1237,6 +1236,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
        if (error)
                goto out;
 
+retry_ref:
        lock_buffer(bh);
        hash = le32_to_cpu(BHDR(bh)->h_hash);
        ref = le32_to_cpu(BHDR(bh)->h_refcount);
@@ -1246,9 +1246,18 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                 * This must happen under buffer lock for
                 * ext4_xattr_block_set() to reliably detect freed block
                 */
-               if (ea_block_cache)
-                       mb_cache_entry_delete(ea_block_cache, hash,
-                                             bh->b_blocknr);
+               if (ea_block_cache) {
+                       struct mb_cache_entry *oe;
+
+                       oe = mb_cache_entry_delete_or_get(ea_block_cache, hash,
+                                                         bh->b_blocknr);
+                       if (oe) {
+                               unlock_buffer(bh);
+                               mb_cache_entry_wait_unused(oe);
+                               mb_cache_entry_put(ea_block_cache, oe);
+                               goto retry_ref;
+                       }
+               }
                get_bh(bh);
                unlock_buffer(bh);
 
@@ -1858,6 +1867,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
 #define header(x) ((struct ext4_xattr_header *)(x))
 
        if (s->base) {
+               int offset = (char *)s->here - bs->bh->b_data;
+
                BUFFER_TRACE(bs->bh, "get_write_access");
                error = ext4_journal_get_write_access(handle, sb, bs->bh,
                                                      EXT4_JTR_NONE);
@@ -1873,9 +1884,20 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                         * ext4_xattr_block_set() to reliably detect modified
                         * block
                         */
-                       if (ea_block_cache)
-                               mb_cache_entry_delete(ea_block_cache, hash,
-                                                     bs->bh->b_blocknr);
+                       if (ea_block_cache) {
+                               struct mb_cache_entry *oe;
+
+                               oe = mb_cache_entry_delete_or_get(ea_block_cache,
+                                       hash, bs->bh->b_blocknr);
+                               if (oe) {
+                                       /*
+                                        * Xattr block is getting reused. Leave
+                                        * it alone.
+                                        */
+                                       mb_cache_entry_put(ea_block_cache, oe);
+                                       goto clone_block;
+                               }
+                       }
                        ea_bdebug(bs->bh, "modifying in-place");
                        error = ext4_xattr_set_entry(i, s, handle, inode,
                                                     true /* is_block */);
@@ -1890,49 +1912,47 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                        if (error)
                                goto cleanup;
                        goto inserted;
-               } else {
-                       int offset = (char *)s->here - bs->bh->b_data;
+               }
+clone_block:
+               unlock_buffer(bs->bh);
+               ea_bdebug(bs->bh, "cloning");
+               s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS);
+               error = -ENOMEM;
+               if (s->base == NULL)
+                       goto cleanup;
+               s->first = ENTRY(header(s->base)+1);
+               header(s->base)->h_refcount = cpu_to_le32(1);
+               s->here = ENTRY(s->base + offset);
+               s->end = s->base + bs->bh->b_size;
 
-                       unlock_buffer(bs->bh);
-                       ea_bdebug(bs->bh, "cloning");
-                       s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS);
-                       error = -ENOMEM;
-                       if (s->base == NULL)
+               /*
+                * If existing entry points to an xattr inode, we need
+                * to prevent ext4_xattr_set_entry() from decrementing
+                * ref count on it because the reference belongs to the
+                * original block. In this case, make the entry look
+                * like it has an empty value.
+                */
+               if (!s->not_found && s->here->e_value_inum) {
+                       ea_ino = le32_to_cpu(s->here->e_value_inum);
+                       error = ext4_xattr_inode_iget(inode, ea_ino,
+                                     le32_to_cpu(s->here->e_hash),
+                                     &tmp_inode);
+                       if (error)
                                goto cleanup;
-                       s->first = ENTRY(header(s->base)+1);
-                       header(s->base)->h_refcount = cpu_to_le32(1);
-                       s->here = ENTRY(s->base + offset);
-                       s->end = s->base + bs->bh->b_size;
-
-                       /*
-                        * If existing entry points to an xattr inode, we need
-                        * to prevent ext4_xattr_set_entry() from decrementing
-                        * ref count on it because the reference belongs to the
-                        * original block. In this case, make the entry look
-                        * like it has an empty value.
-                        */
-                       if (!s->not_found && s->here->e_value_inum) {
-                               ea_ino = le32_to_cpu(s->here->e_value_inum);
-                               error = ext4_xattr_inode_iget(inode, ea_ino,
-                                             le32_to_cpu(s->here->e_hash),
-                                             &tmp_inode);
-                               if (error)
-                                       goto cleanup;
-
-                               if (!ext4_test_inode_state(tmp_inode,
-                                               EXT4_STATE_LUSTRE_EA_INODE)) {
-                                       /*
-                                        * Defer quota free call for previous
-                                        * inode until success is guaranteed.
-                                        */
-                                       old_ea_inode_quota = le32_to_cpu(
-                                                       s->here->e_value_size);
-                               }
-                               iput(tmp_inode);
 
-                               s->here->e_value_inum = 0;
-                               s->here->e_value_size = 0;
+                       if (!ext4_test_inode_state(tmp_inode,
+                                       EXT4_STATE_LUSTRE_EA_INODE)) {
+                               /*
+                                * Defer quota free call for previous
+                                * inode until success is guaranteed.
+                                */
+                               old_ea_inode_quota = le32_to_cpu(
+                                               s->here->e_value_size);
                        }
+                       iput(tmp_inode);
+
+                       s->here->e_value_inum = 0;
+                       s->here->e_value_size = 0;
                }
        } else {
                /* Allocate a buffer where we construct the new block. */
@@ -1999,18 +2019,13 @@ inserted:
                                lock_buffer(new_bh);
                                /*
                                 * We have to be careful about races with
-                                * freeing, rehashing or adding references to
-                                * xattr block. Once we hold buffer lock xattr
-                                * block's state is stable so we can check
-                                * whether the block got freed / rehashed or
-                                * not.  Since we unhash mbcache entry under
-                                * buffer lock when freeing / rehashing xattr
-                                * block, checking whether entry is still
-                                * hashed is reliable. Same rules hold for
-                                * e_reusable handling.
+                                * adding references to xattr block. Once we
+                                * hold buffer lock xattr block's state is
+                                * stable so we can check the additional
+                                * reference fits.
                                 */
-                               if (hlist_bl_unhashed(&ce->e_hash_list) ||
-                                   !ce->e_reusable) {
+                               ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1;
+                               if (ref > EXT4_XATTR_REFCOUNT_MAX) {
                                        /*
                                         * Undo everything and check mbcache
                                         * again.
@@ -2025,9 +2040,8 @@ inserted:
                                        new_bh = NULL;
                                        goto inserted;
                                }
-                               ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1;
                                BHDR(new_bh)->h_refcount = cpu_to_le32(ref);
-                               if (ref >= EXT4_XATTR_REFCOUNT_MAX)
+                               if (ref == EXT4_XATTR_REFCOUNT_MAX)
                                        ce->e_reusable = 0;
                                ea_bdebug(new_bh, "reusing; refcount now=%d",
                                          ref);
@@ -2175,8 +2189,9 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i,
        struct ext4_inode *raw_inode;
        int error;
 
-       if (EXT4_I(inode)->i_extra_isize == 0)
+       if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
                return 0;
+
        raw_inode = ext4_raw_inode(&is->iloc);
        header = IHDR(inode, raw_inode);
        is->s.base = is->s.first = IFIRST(header);
@@ -2204,8 +2219,9 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode,
        struct ext4_xattr_search *s = &is->s;
        int error;
 
-       if (EXT4_I(inode)->i_extra_isize == 0)
+       if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
                return -ENOSPC;
+
        error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */);
        if (error)
                return error;