The NFS mounting jffs2 filesystem with errors, solution need
David Woodhouse
dwmw2 at infradead.org
Thu Mar 8 11:57:11 EST 2007
On Thu, 2007-03-08 at 15:08 +0000, David Woodhouse wrote:
> [C00000000A5C34F0] [D0000000009B99A8] .jffs2_lookup+0x88/0x218 [jffs2]
> [C00000000A5C35A0] [C0000000001056F4] .__lookup_hash+0x18c/0x1d4
> [C00000000A5C3640] [C000000000105E0C] .lookup_one_len+0x74/0x8c
> [C00000000A5C36D0] [D000000000AD0B6C] .compose_entry_fh+0x12c/0x1bc [nfsd]
> [C00000000A5C3780] [D000000000AD0EBC] .encode_entry+0x1dc/0x444 [nfsd]
> [C00000000A5C3AA0] [D0000000009B9CF0] .jffs2_readdir+0x1b8/0x24c [jffs2]
OK, it's caused by readdirplus, which seems to call our ->lookup() on
every child of the directory it's reading, just to generate a filehandle
for each one.
This problem isn't limited to JFFS2. GFS2 works around it by checking
whether the lock owner == current, and GFS1 does it by building up a
list of everything it wants to return, then dropping all the locks and
feeding it all to the filldir() function afterwards.
If we could just provide i_generation to filldir(), the NFS code
wouldn't _need_ to actually call ->lookup() for every child inode; it
could create the filehandles for itself. At least in the common case
where the filesystem doesn't override encode_fh().
This evil hack should work around the problem, but I'd rather see a
proper fix.
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c
index 9fa2e27..a1f62ec 100644
--- a/fs/jffs2/dir.c
+++ b/fs/jffs2/dir.c
@@ -88,7 +88,8 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
dir_f = JFFS2_INODE_INFO(dir_i);
c = JFFS2_SB_INFO(dir_i->i_sb);
- down(&dir_f->sem);
+ if (current != dir_f->readdir_process)
+ down(&dir_f->sem);
/* NB: The 2.2 backport will need to explicitly check for '.' and '..' here */
for (fd_list = dir_f->dents; fd_list && fd_list->nhash <= target->d_name.hash; fd_list = fd_list->next) {
@@ -101,7 +102,8 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
}
if (fd)
ino = fd->ino;
- up(&dir_f->sem);
+ if (current != dir_f->readdir_process)
+ up(&dir_f->sem);
if (ino) {
inode = iget(dir_i->i_sb, ino);
if (!inode) {
@@ -149,6 +151,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
curofs=1;
down(&f->sem);
+ f->readdir_process = current;
for (fd = f->dents; fd; fd = fd->next) {
curofs++;
@@ -168,6 +171,7 @@ static int jffs2_readdir(struct file *filp, void *dirent, filldir_t filldir)
break;
offset++;
}
+ f->readdir_process = NULL;
up(&f->sem);
out:
filp->f_pos = offset;
diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h
index 3a56607..811a0ea 100644
--- a/fs/jffs2/jffs2_fs_i.h
+++ b/fs/jffs2/jffs2_fs_i.h
@@ -16,7 +16,7 @@ struct jffs2_inode_info {
into the GC code so it didn't attempt to obtain the i_mutex
for the inode(s) which are already locked */
struct semaphore sem;
-
+ struct task_struct *readdir_process;
/* The highest (datanode) version number used for this ino */
uint32_t highest_version;
--
dwmw2
More information about the linux-mtd
mailing list