Messages in this thread | | | Date | Sat, 22 May 1999 13:27:58 -0500 (CDT) | From | Tom Shield <> | Subject | Possible NFS client 2.2.9 kernel bug and fix |
| |
Hello,
Here is the bug I ran across:
NFS mount from server (nfs-server-2.2beta37, kernel 2.2.6, glibc) to a 2.2.9 kernel glibc (RH5.2+) machine. Initially found that on a find of all files on a nfs mount, find used up all swap and caused nasty things to happen. Tracked this down to a single directory that had an infinite loop in its entries (readdir() keeps returning the directory entries over and over again). I checked the following:
directory is fine on server (actually a read only mount of an NTFS) directory mounted on a 2.0.36 kernel (RH 4.2+, libc) box is ok
Thus I figured it had to be the 2.2.9 (2.2.7 and 2.2.8 also) kernel.
So I hacked my way into fs/nfs/dir.c and traced it to the EOF (bit 15) bit not being set on the last entry returned by the nfs_proc_readdir. I don't know sunrpc (not that I know NFS either ;), thus it got too dense for me to dig deeper. So I fixed it at that point. The attached patch addeds a check for the EOF bit on the last directory entry and sets it if it is not set on the initial read of the directory before it is put in the cache. When it was not set the cache reading code never finds the directory entry in the cache (it falls off the bottom of the j loop thru the entires looking for the EOF bit set), so the dir is fetched again and again it is not found in the cache, giving the infinite loop.
Someone might want to think about the consequences of hiting the break at line 207 in dir.c, which AFAIK should never be hit. There does seem to be an inconsistancy in the code that two different methods are used to signal the end of the directory, initially size and then the EOF bit when reading from the cache. When find used up all swap on this loop lots of bad things happend, processes died, etc.
This is a hard error, so I can test any better fix that comes along. However, my patch might be an addition to the NFS_PARANOIA as the consequences are rather severe. But I don't think I've found the original cause of this behavior.
thanks for your help,
Tom Shield Aerospace Engineering and Mechanics University of Minnesota (612) 626-7793 http://www.aem.umn.edu/people/faculty/shield/ diff -C 2 -P /usr/tmp/nfs/dir.c fs/nfs/dir.c *** /usr/tmp/nfs/dir.c Wed May 12 17:10:34 1999 --- fs/nfs/dir.c Thu May 20 19:38:05 1999 *************** *** 246,249 **** --- 246,267 ---- cache->valid = 1; entry = cache->entry + (index = 0); + + /* make sure the last one has EOF set + this avoids the possibility of a nasty inf loop -- shield@aem.umn.edu */ + + { + __u32 *this_ent = cache->entry + 3*cache->size - 3; + + if (*(this_ent+2) & (1 << 15)) + { + dfprintk(VFS, "TWS: found EOF \n"); + } + else + { + *(this_ent+2) = *(this_ent+2) | (1 << 15); + dfprintk(VFS, "TWS: SET EOF bit \n"); + } + } + } cache->mtime = inode->i_mtime;
| |