OSDN Git Service

Merge tag 'v4.4.214' into 10
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / nfs / dir.c
index c690a1c..2ac3d25 100644 (file)
@@ -169,6 +169,17 @@ typedef struct {
        unsigned int    eof:1;
 } nfs_readdir_descriptor_t;
 
+static
+void nfs_readdir_init_array(struct page *page)
+{
+       struct nfs_cache_array *array;
+
+       array = kmap_atomic(page);
+       memset(array, 0, sizeof(struct nfs_cache_array));
+       array->eof_index = -1;
+       kunmap_atomic(array);
+}
+
 /*
  * The caller is responsible for calling nfs_readdir_release_array(page)
  */
@@ -202,6 +213,7 @@ void nfs_readdir_clear_array(struct page *page)
        array = kmap_atomic(page);
        for (i = 0; i < array->size; i++)
                kfree(array->array[i].string.name);
+       array->size = 0;
        kunmap_atomic(array);
 }
 
@@ -277,7 +289,7 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri
        desc->cache_entry_index = index;
        return 0;
 out_eof:
-       desc->eof = 1;
+       desc->eof = true;
        return -EBADCOOKIE;
 }
 
@@ -331,7 +343,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des
        if (array->eof_index >= 0) {
                status = -EBADCOOKIE;
                if (*desc->dir_cookie == array->last_cookie)
-                       desc->eof = 1;
+                       desc->eof = true;
        }
 out:
        return status;
@@ -622,6 +634,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        int status = -ENOMEM;
        unsigned int array_size = ARRAY_SIZE(pages);
 
+       nfs_readdir_init_array(page);
+
        entry.prev_cookie = 0;
        entry.cookie = desc->last_cookie;
        entry.eof = 0;
@@ -642,8 +656,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
                status = PTR_ERR(array);
                goto out_label_free;
        }
-       memset(array, 0, sizeof(struct nfs_cache_array));
-       array->eof_index = -1;
+
+       array = kmap(page);
 
        status = nfs_readdir_alloc_pages(pages, array_size);
        if (status < 0)
@@ -698,6 +712,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
        unlock_page(page);
        return 0;
  error:
+       nfs_readdir_clear_array(page);
        unlock_page(page);
        return ret;
 }
@@ -705,8 +720,6 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page)
 static
 void cache_page_release(nfs_readdir_descriptor_t *desc)
 {
-       if (!desc->page->mapping)
-               nfs_readdir_clear_array(desc->page);
        page_cache_release(desc->page);
        desc->page = NULL;
 }
@@ -720,19 +733,28 @@ struct page *get_cache_page(nfs_readdir_descriptor_t *desc)
 
 /*
  * Returns 0 if desc->dir_cookie was found on page desc->page_index
+ * and locks the page to prevent removal from the page cache.
  */
 static
-int find_cache_page(nfs_readdir_descriptor_t *desc)
+int find_and_lock_cache_page(nfs_readdir_descriptor_t *desc)
 {
        int res;
 
        desc->page = get_cache_page(desc);
        if (IS_ERR(desc->page))
                return PTR_ERR(desc->page);
-
-       res = nfs_readdir_search_array(desc);
+       res = lock_page_killable(desc->page);
        if (res != 0)
-               cache_page_release(desc);
+               goto error;
+       res = -EAGAIN;
+       if (desc->page->mapping != NULL) {
+               res = nfs_readdir_search_array(desc);
+               if (res == 0)
+                       return 0;
+       }
+       unlock_page(desc->page);
+error:
+       cache_page_release(desc);
        return res;
 }
 
@@ -747,7 +769,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
                desc->last_cookie = 0;
        }
        do {
-               res = find_cache_page(desc);
+               res = find_and_lock_cache_page(desc);
        } while (res == -EAGAIN);
        return res;
 }
@@ -776,7 +798,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
                ent = &array->array[i];
                if (!dir_emit(desc->ctx, ent->string.name, ent->string.len,
                    nfs_compat_user_ino64(ent->ino), ent->d_type)) {
-                       desc->eof = 1;
+                       desc->eof = true;
                        break;
                }
                desc->ctx->pos++;
@@ -788,11 +810,10 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
                        ctx->duped = 1;
        }
        if (array->eof_index >= 0)
-               desc->eof = 1;
+               desc->eof = true;
 
        nfs_readdir_release_array(desc->page);
 out:
-       cache_page_release(desc);
        dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
                        (unsigned long long)*desc->dir_cookie, res);
        return res;
@@ -838,13 +859,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
 
        status = nfs_do_filldir(desc);
 
+ out_release:
+       nfs_readdir_clear_array(desc->page);
+       cache_page_release(desc);
  out:
        dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
                        __func__, status);
        return status;
- out_release:
-       cache_page_release(desc);
-       goto out;
 }
 
 /* The file offset position represents the dirent entry number.  A
@@ -890,7 +911,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
                if (res == -EBADCOOKIE) {
                        res = 0;
                        /* This means either end of directory */
-                       if (*desc->dir_cookie && desc->eof == 0) {
+                       if (*desc->dir_cookie && !desc->eof) {
                                /* Or that the server has 'lost' a cookie */
                                res = uncached_readdir(desc);
                                if (res == 0)
@@ -910,6 +931,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
                        break;
 
                res = nfs_do_filldir(desc);
+               unlock_page(desc->page);
+               cache_page_release(desc);
                if (res < 0)
                        break;
        } while (!desc->eof);