From e65b147a9e1d496bf1b4266e796f567f36ba4ab4 Mon Sep 17 00:00:00 2001 From: Rachita Kothiyal Date: Sun, 18 May 2008 03:37:20 -0400 Subject: [PATCH] Unionfs ODF: Fix a lockdep warning on nfsd_readdir of exported unionfs mount nfsd_readdir calls vfs_llseek and vfs_readdir. Following is the locking order seen by lockdep: vfs_llseek: sb -> dentry -> inode vfs_readdir: inode -> sb -> dentry Lockdep reports this as a possible circular locking order bug. To fix this, we define our own unionfs_file_llseek_locked function, which is essentially the vfs generic_file_llseek function, but with the inode locking moved into the caller. This now changes the locking order in vfs_llseek call to be: inode -> sb -> dentry, and satisfies lockdep too. Signed-off-by: Rachita Kothiyal --- fs/unionfs/dirfops.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c index 8e578af511..27b55ca7a0 100644 --- a/fs/unionfs/dirfops.c +++ b/fs/unionfs/dirfops.c @@ -137,11 +137,48 @@ out: return err; } +/* + * Heavily borrowed from the vfs generic_file_llseek. This version expects + * an already locked inode. This was done to fix a lockdep warning arising + * from the following locking order in nfs_readdir on an exported unionfs + * mount: + * nfs_readdir + * --> vfs_llseek(): sb -> dentry -> inode + * --> vfs_readdir(): inode -> sb -> dentry + * + * A better fix would be to have this as a vfs function. + */ +static loff_t unionfs_file_llseek_locked(struct file *file, loff_t offset, + int origin) +{ + long long retval; + struct inode *inode = file->f_mapping->host; + + switch (origin) { + case SEEK_END: + offset += inode->i_size; + break; + case SEEK_CUR: + offset += file->f_pos; + } + retval = -EINVAL; + if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) { + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_version = 0; + } + retval = offset; + } + return retval; +} + static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin) { struct dentry *dentry = file->f_path.dentry; + struct inode *inode = dentry->d_inode; loff_t err; + mutex_lock(&inode->i_mutex); unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); @@ -150,9 +187,10 @@ static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin) if (unlikely(err)) goto out; - err = generic_file_llseek(file, offset, origin); + err = unionfs_file_llseek_locked(file, offset, origin); out: unionfs_read_unlock(dentry->d_sb); + mutex_unlock(&inode->i_mutex); return err; } -- 2.34.1