[RFC PATCH] NFS: Fix missing files in `ls` command output

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In our production environment, we noticed that some files are missing when
running the ls command in an NFS directory. However, we can still
successfully cd into the missing directories. This issue can be illustrated
as follows:

  $ cd nfs
  $ ls
  a b c e f            <<<< 'd' is missing
  $ cd d               <<<< success

I verified the issue with the latest upstream kernel, and it still
persists. Further analysis reveals that files go missing when the dtsize is
expanded. The default dtsize was reduced from 1MB to 4KB in commit
580f236737d1 ("NFS: Adjust the amount of readahead performed by NFS readdir").
After restoring the default size to 1MB, the issue disappears. I also tried
setting the default size to 8KB, and the issue similarly disappears.

Upon further analysis, it appears that there is a bad entry being decoded
in nfs_readdir_entry_decode(). When a bad entry is encountered, the
decoding process breaks without handling the error. We should revert the
bad entry in such cases. After implementing this change, the issue is
resolved.

However, I am unable to reproduce this issue with a simple example; it only
occurs on our production servers.

Signed-off-by: Yafang Shao <laoar.shao@xxxxxxxxx>
---
 fs/nfs/dir.c | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 07a7be27182e..1f5a99888a11 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -310,7 +310,7 @@ static int nfs_readdir_array_can_expand(struct nfs_cache_array *array)
 
 static int nfs_readdir_folio_array_append(struct folio *folio,
 					  const struct nfs_entry *entry,
-					  u64 *cookie)
+					  u64 *cookie, u64 *prev_cookie)
 {
 	struct nfs_cache_array *array;
 	struct nfs_cache_array_entry *cache_entry;
@@ -342,6 +342,7 @@ static int nfs_readdir_folio_array_append(struct folio *folio,
 		nfs_readdir_array_set_eof(array);
 out:
 	*cookie = array->last_cookie;
+	*prev_cookie = cache_entry->cookie;
 	kunmap_local(array);
 	return ret;
 }
@@ -826,10 +827,11 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
 {
 	struct address_space *mapping = desc->file->f_mapping;
 	struct folio *new, *folio = *arrays;
+	struct nfs_cache_array *array;
 	struct xdr_stream stream;
+	u64 cookie, prev_cookie;
 	struct page *scratch;
 	struct xdr_buf buf;
-	u64 cookie;
 	int status;
 
 	scratch = alloc_page(GFP_KERNEL);
@@ -841,10 +843,20 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
 
 	do {
 		status = nfs_readdir_entry_decode(desc, entry, &stream);
-		if (status != 0)
+		if (status != 0) {
+			if (status == -EAGAIN && entry->cookie == cookie) {
+				/* Revert the bad entry */
+				array = kmap_local_folio(folio, 0);
+				array->last_cookie = prev_cookie;
+				desc->last_cookie = 0;
+				desc->dir_cookie = 0;
+				array->size--;
+				kunmap_local(array);
+			}
 			break;
+		}
 
-		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
+		status = nfs_readdir_folio_array_append(folio, entry, &cookie, &prev_cookie);
 		if (status != -ENOSPC)
 			continue;
 
@@ -866,7 +878,7 @@ static int nfs_readdir_folio_filler(struct nfs_readdir_descriptor *desc,
 			folio = new;
 		}
 		desc->folio_index_max++;
-		status = nfs_readdir_folio_array_append(folio, entry, &cookie);
+		status = nfs_readdir_folio_array_append(folio, entry, &cookie, &prev_cookie);
 	} while (!status && !entry->eof);
 
 	switch (status) {
-- 
2.30.1 (Apple Git-130)





[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux