OSDN Git Service

Merge 4.4.161 into android-4.4
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / ext4 / xattr.c
index d0aaf33..4d0365a 100644 (file)
@@ -53,7 +53,7 @@
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/slab.h>
-#include <linux/mbcache.h>
+#include <linux/mbcache2.h>
 #include <linux/quotaops.h>
 #include "ext4_jbd2.h"
 #include "ext4.h"
 # define ea_bdebug(bh, fmt, ...)       no_printk(fmt, ##__VA_ARGS__)
 #endif
 
-static void ext4_xattr_cache_insert(struct mb_cache *, struct buffer_head *);
+static void ext4_xattr_cache_insert(struct mb2_cache *, struct buffer_head *);
 static struct buffer_head *ext4_xattr_cache_find(struct inode *,
                                                 struct ext4_xattr_header *,
-                                                struct mb_cache_entry **);
+                                                struct mb2_cache_entry **);
 static void ext4_xattr_rehash(struct ext4_xattr_header *,
                              struct ext4_xattr_entry *);
 static int ext4_xattr_list(struct dentry *dentry, char *buffer,
@@ -302,7 +302,7 @@ ext4_xattr_block_get(struct inode *inode, int name_index, const char *name,
        struct ext4_xattr_entry *entry;
        size_t size;
        int error;
-       struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+       struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
 
        ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
                  name_index, name, buffer, (long)buffer_size);
@@ -449,7 +449,7 @@ ext4_xattr_block_list(struct dentry *dentry, char *buffer, size_t buffer_size)
        struct inode *inode = d_inode(dentry);
        struct buffer_head *bh = NULL;
        int error;
-       struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+       struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
 
        ea_idebug(inode, "buffer=%p, buffer_size=%ld",
                  buffer, (long)buffer_size);
@@ -566,11 +566,8 @@ static void
 ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                         struct buffer_head *bh)
 {
-       struct mb_cache_entry *ce = NULL;
        int error = 0;
-       struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
 
-       ce = mb_cache_entry_get(ext4_mb_cache, bh->b_bdev, bh->b_blocknr);
        BUFFER_TRACE(bh, "get_write_access");
        error = ext4_journal_get_write_access(handle, bh);
        if (error)
@@ -578,9 +575,15 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
 
        lock_buffer(bh);
        if (BHDR(bh)->h_refcount == cpu_to_le32(1)) {
+               __u32 hash = le32_to_cpu(BHDR(bh)->h_hash);
+
                ea_bdebug(bh, "refcount now=0; freeing");
-               if (ce)
-                       mb_cache_entry_free(ce);
+               /*
+                * This must happen under buffer lock for
+                * ext4_xattr_block_set() to reliably detect freed block
+                */
+               mb2_cache_entry_delete_block(EXT4_GET_MB_CACHE(inode), hash,
+                                            bh->b_blocknr);
                get_bh(bh);
                unlock_buffer(bh);
                ext4_free_blocks(handle, inode, bh, 0, 1,
@@ -588,8 +591,6 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
                                 EXT4_FREE_BLOCKS_FORGET);
        } else {
                le32_add_cpu(&BHDR(bh)->h_refcount, -1);
-               if (ce)
-                       mb_cache_entry_release(ce);
                /*
                 * Beware of this ugliness: Releasing of xattr block references
                 * from different inodes can race and so we have to protect
@@ -802,17 +803,15 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
        struct super_block *sb = inode->i_sb;
        struct buffer_head *new_bh = NULL;
        struct ext4_xattr_search *s = &bs->s;
-       struct mb_cache_entry *ce = NULL;
+       struct mb2_cache_entry *ce = NULL;
        int error = 0;
-       struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+       struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
 
 #define header(x) ((struct ext4_xattr_header *)(x))
 
        if (i->value && i->value_len > sb->s_blocksize)
                return -ENOSPC;
        if (s->base) {
-               ce = mb_cache_entry_get(ext4_mb_cache, bs->bh->b_bdev,
-                                       bs->bh->b_blocknr);
                BUFFER_TRACE(bs->bh, "get_write_access");
                error = ext4_journal_get_write_access(handle, bs->bh);
                if (error)
@@ -820,10 +819,15 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                lock_buffer(bs->bh);
 
                if (header(s->base)->h_refcount == cpu_to_le32(1)) {
-                       if (ce) {
-                               mb_cache_entry_free(ce);
-                               ce = NULL;
-                       }
+                       __u32 hash = le32_to_cpu(BHDR(bs->bh)->h_hash);
+
+                       /*
+                        * This must happen under buffer lock for
+                        * ext4_xattr_block_set() to reliably detect modified
+                        * block
+                        */
+                       mb2_cache_entry_delete_block(ext4_mb_cache, hash,
+                                                    bs->bh->b_blocknr);
                        ea_bdebug(bs->bh, "modifying in-place");
                        error = ext4_xattr_set_entry(i, s);
                        if (!error) {
@@ -845,10 +849,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                        int offset = (char *)s->here - bs->bh->b_data;
 
                        unlock_buffer(bs->bh);
-                       if (ce) {
-                               mb_cache_entry_release(ce);
-                               ce = NULL;
-                       }
                        ea_bdebug(bs->bh, "cloning");
                        s->base = kmalloc(bs->bh->b_size, GFP_NOFS);
                        error = -ENOMEM;
@@ -903,6 +903,31 @@ inserted:
                                if (error)
                                        goto cleanup_dquot;
                                lock_buffer(new_bh);
+                               /*
+                                * We have to be careful about races with
+                                * freeing or rehashing of 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.
+                                */
+                               if (hlist_bl_unhashed(&ce->e_hash_list)) {
+                                       /*
+                                        * Undo everything and check mbcache
+                                        * again.
+                                        */
+                                       unlock_buffer(new_bh);
+                                       dquot_free_block(inode,
+                                                        EXT4_C2B(EXT4_SB(sb),
+                                                                 1));
+                                       brelse(new_bh);
+                                       mb2_cache_entry_put(ext4_mb_cache, ce);
+                                       ce = NULL;
+                                       new_bh = NULL;
+                                       goto inserted;
+                               }
                                le32_add_cpu(&BHDR(new_bh)->h_refcount, 1);
                                ea_bdebug(new_bh, "reusing; refcount now=%d",
                                        le32_to_cpu(BHDR(new_bh)->h_refcount));
@@ -913,7 +938,8 @@ inserted:
                                if (error)
                                        goto cleanup_dquot;
                        }
-                       mb_cache_entry_release(ce);
+                       mb2_cache_entry_touch(ext4_mb_cache, ce);
+                       mb2_cache_entry_put(ext4_mb_cache, ce);
                        ce = NULL;
                } else if (bs->bh && s->base == bs->bh->b_data) {
                        /* We were modifying this block in-place. */
@@ -979,7 +1005,7 @@ getblk_failed:
 
 cleanup:
        if (ce)
-               mb_cache_entry_release(ce);
+               mb2_cache_entry_put(ext4_mb_cache, ce);
        brelse(new_bh);
        if (!(bs->bh && s->base == bs->bh->b_data))
                kfree(s->base);
@@ -1543,17 +1569,6 @@ cleanup:
 }
 
 /*
- * ext4_xattr_put_super()
- *
- * This is called when a file system is unmounted.
- */
-void
-ext4_xattr_put_super(struct super_block *sb)
-{
-       mb_cache_shrink(sb->s_bdev);
-}
-
-/*
  * ext4_xattr_cache_insert()
  *
  * Create a new entry in the extended attribute cache, and insert
@@ -1562,28 +1577,18 @@ ext4_xattr_put_super(struct super_block *sb)
  * Returns 0, or a negative error number on failure.
  */
 static void
-ext4_xattr_cache_insert(struct mb_cache *ext4_mb_cache, struct buffer_head *bh)
+ext4_xattr_cache_insert(struct mb2_cache *ext4_mb_cache, struct buffer_head *bh)
 {
        __u32 hash = le32_to_cpu(BHDR(bh)->h_hash);
-       struct mb_cache_entry *ce;
        int error;
 
-       ce = mb_cache_entry_alloc(ext4_mb_cache, GFP_NOFS);
-       if (!ce) {
-               ea_bdebug(bh, "out of memory");
-               return;
-       }
-       error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash);
+       error = mb2_cache_entry_create(ext4_mb_cache, GFP_NOFS, hash,
+                                      bh->b_blocknr);
        if (error) {
-               mb_cache_entry_free(ce);
-               if (error == -EBUSY) {
+               if (error == -EBUSY)
                        ea_bdebug(bh, "already in cache");
-                       error = 0;
-               }
-       } else {
+       } else
                ea_bdebug(bh, "inserting [%x]", (int)hash);
-               mb_cache_entry_release(ce);
-       }
 }
 
 /*
@@ -1636,26 +1641,19 @@ ext4_xattr_cmp(struct ext4_xattr_header *header1,
  */
 static struct buffer_head *
 ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header,
-                     struct mb_cache_entry **pce)
+                     struct mb2_cache_entry **pce)
 {
        __u32 hash = le32_to_cpu(header->h_hash);
-       struct mb_cache_entry *ce;
-       struct mb_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
+       struct mb2_cache_entry *ce;
+       struct mb2_cache *ext4_mb_cache = EXT4_GET_MB_CACHE(inode);
 
        if (!header->h_hash)
                return NULL;  /* never share */
        ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
-again:
-       ce = mb_cache_entry_find_first(ext4_mb_cache, inode->i_sb->s_bdev,
-                                      hash);
+       ce = mb2_cache_entry_find_first(ext4_mb_cache, hash);
        while (ce) {
                struct buffer_head *bh;
 
-               if (IS_ERR(ce)) {
-                       if (PTR_ERR(ce) == -EAGAIN)
-                               goto again;
-                       break;
-               }
                bh = sb_bread(inode->i_sb, ce->e_block);
                if (!bh) {
                        EXT4_ERROR_INODE(inode, "block %lu read error",
@@ -1671,7 +1669,7 @@ again:
                        return bh;
                }
                brelse(bh);
-               ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash);
+               ce = mb2_cache_entry_find_next(ext4_mb_cache, ce);
        }
        return NULL;
 }
@@ -1746,15 +1744,15 @@ static void ext4_xattr_rehash(struct ext4_xattr_header *header,
 
 #define        HASH_BUCKET_BITS        10
 
-struct mb_cache *
-ext4_xattr_create_cache(char *name)
+struct mb2_cache *
+ext4_xattr_create_cache(void)
 {
-       return mb_cache_create(name, HASH_BUCKET_BITS);
+       return mb2_cache_create(HASH_BUCKET_BITS);
 }
 
-void ext4_xattr_destroy_cache(struct mb_cache *cache)
+void ext4_xattr_destroy_cache(struct mb2_cache *cache)
 {
        if (cache)
-               mb_cache_destroy(cache);
+               mb2_cache_destroy(cache);
 }