]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/nfs/dir.c
Merge tag 'mac80211-for-net-2020-02-14' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / fs / nfs / dir.c
index 6427a8a8d61a9bc571fdb2263a0de976d0531fee..1320288ff9ec9c7d50d207908d77f3899c7def3b 100644 (file)
@@ -58,7 +58,7 @@ static void nfs_readdir_clear_array(struct page*);
 const struct file_operations nfs_dir_operations = {
        .llseek         = nfs_llseek_dir,
        .read           = generic_read_dir,
-       .iterate        = nfs_readdir,
+       .iterate_shared = nfs_readdir,
        .open           = nfs_opendir,
        .release        = nfs_closedir,
        .fsync          = nfs_fsync_dir,
@@ -162,6 +162,17 @@ typedef struct {
        bool eof;
 } 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);
+}
+
 /*
  * we are freeing strings created by nfs_add_to_readdir_array()
  */
@@ -174,6 +185,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);
 }
 
@@ -186,7 +198,7 @@ static
 int nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len)
 {
        string->len = len;
-       string->name = kmemdup(name, len, GFP_KERNEL);
+       string->name = kmemdup_nul(name, len, GFP_KERNEL);
        if (string->name == NULL)
                return -ENOMEM;
        /*
@@ -437,7 +449,8 @@ void nfs_force_use_readdirplus(struct inode *dir)
        if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
            !list_empty(&nfsi->open_files)) {
                set_bit(NFS_INO_ADVISE_RDPLUS, &nfsi->flags);
-               invalidate_mapping_pages(dir->i_mapping, 0, -1);
+               invalidate_mapping_pages(dir->i_mapping,
+                       nfsi->page_index + 1, -1);
        }
 }
 
@@ -610,6 +623,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;
@@ -626,8 +641,6 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
        }
 
        array = kmap(page);
-       memset(array, 0, sizeof(struct nfs_cache_array));
-       array->eof_index = -1;
 
        status = nfs_readdir_alloc_pages(pages, array_size);
        if (status < 0)
@@ -682,6 +695,7 @@ int nfs_readdir_filler(void *data, struct page* page)
        unlock_page(page);
        return 0;
  error:
+       nfs_readdir_clear_array(page);
        unlock_page(page);
        return ret;
 }
@@ -689,8 +703,6 @@ int nfs_readdir_filler(void *data, struct page* page)
 static
 void cache_page_release(nfs_readdir_descriptor_t *desc)
 {
-       if (!desc->page->mapping)
-               nfs_readdir_clear_array(desc->page);
        put_page(desc->page);
        desc->page = NULL;
 }
@@ -704,19 +716,32 @@ 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)
 {
+       struct inode *inode = file_inode(desc->file);
+       struct nfs_inode *nfsi = NFS_I(inode);
        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) {
+                       nfsi->page_index = desc->page_index;
+                       return 0;
+               }
+       }
+       unlock_page(desc->page);
+error:
+       cache_page_release(desc);
        return res;
 }
 
@@ -731,7 +756,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;
 }
@@ -770,7 +795,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
                desc->eof = true;
 
        kunmap(desc->page);
-       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;
@@ -816,13 +840,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
@@ -887,6 +911,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);