From: "J. Bruce Fields" <bfields@fieldses.org>
To: David Woodhouse <dwmw2@infradead.org>
Cc: Al Viro <viro@ZenIV.linux.org.uk>,
hooanon05@yahoo.co.jp, hch@infradead.org,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"linux-fsdevel@vger.kernel.org" <linux-fsdevel@vger.kernel.org>
Subject: Re: [PATCH v2] Fix i_mutex handling in nfsd readdir
Date: Sun, 19 Apr 2009 16:51:54 -0400 [thread overview]
Message-ID: <20090419205154.GA18110@fieldses.org> (raw)
In-Reply-To: <1240144069.3589.156.camel@macbook.infradead.org>
On Sun, Apr 19, 2009 at 01:27:49PM +0100, David Woodhouse wrote:
> Commit 14f7dd63 ("Copy XFS readdir hack into nfsd code") introduced a
> bug to generic code which had been extant for a long time in the XFS
> version -- it started to call through into lookup_one_len() and hence
> into the file systems' ->lookup() methods without i_mutex held on the
> directory.
>
> This patch fixes it by locking the directory's i_mutex again before
> calling the filldir functions. The original deadlocks which commit
> 14f7dd63 was designed to avoid are still avoided, because they were due
> to fs-internal locking, not i_mutex.
>
> Commit 05f4f678 ("nfsd4: don't do lookup within readdir in recovery
> code") introduced a similar problem there, which this also addresses.
>
> While we're at it, fix the return type of nfsd_buffered_readdir() which
> should be a __be32 not an int -- it's an NFS errno, not a Linux errno.
> And return nfserrno(-ENOMEM) when allocation fails, not just -ENOMEM.
> Sparse would have caught both of those if it wasn't so busy bitching
> about __cold__.
>
> Commit 05f4f678 ("nfsd4: don't do lookup within readdir in recovery
> code") introduced a similar problem with calling lookup_one_len()
> without i_mutex, which this patch also addresses.
>
> Reported-by: J. R. Okajima <hooanon05@yahoo.co.jp>
> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
> Umm-I-can-live-with-that-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
> Still haven't tested the NFSv4 bit -- Bruce?
Thanks, there's an iterator in there that calls a passed-in function,
some examples of which were taking the i_mutex--so some fixing up is
needed. I'll follow up with a patch once I've got one tested.
--b.
>
> This time I remembered to remove the no-longer-used 'done:' label from
> nfsd_buffered_readdir() too.
>
> diff --git a/fs/namei.c b/fs/namei.c
> index b8433eb..78f253c 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -1248,6 +1248,8 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
> int err;
> struct qstr this;
>
> + WARN_ON_ONCE(!mutex_is_locked(&base->d_inode->i_mutex));
> +
> err = __lookup_one_len(name, &this, base, len);
> if (err)
> return ERR_PTR(err);
> diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
> index 3444c00..210709c 100644
> --- a/fs/nfsd/nfs4recover.c
> +++ b/fs/nfsd/nfs4recover.c
> @@ -229,21 +229,23 @@ nfsd4_list_rec_dir(struct dentry *dir, recdir_func *f)
> goto out;
> status = vfs_readdir(filp, nfsd4_build_namelist, &names);
> fput(filp);
> + mutex_lock(&dir->d_inode->i_mutex);
> while (!list_empty(&names)) {
> entry = list_entry(names.next, struct name_list, list);
>
> dentry = lookup_one_len(entry->name, dir, HEXDIR_LEN-1);
> if (IS_ERR(dentry)) {
> status = PTR_ERR(dentry);
> - goto out;
> + break;
> }
> status = f(dir, dentry);
> dput(dentry);
> if (status)
> - goto out;
> + break;
> list_del(&entry->list);
> kfree(entry);
> }
> + mutex_unlock(&dir->d_inode->i_mutex);
> out:
> while (!list_empty(&names)) {
> entry = list_entry(names.next, struct name_list, list);
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index ab93fcf..0874299 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1885,8 +1885,8 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
> return 0;
> }
>
> -static int nfsd_buffered_readdir(struct file *file, filldir_t func,
> - struct readdir_cd *cdp, loff_t *offsetp)
> +static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
> + struct readdir_cd *cdp, loff_t *offsetp)
> {
> struct readdir_data buf;
> struct buffered_dirent *de;
> @@ -1896,11 +1896,12 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
>
> buf.dirent = (void *)__get_free_page(GFP_KERNEL);
> if (!buf.dirent)
> - return -ENOMEM;
> + return nfserrno(-ENOMEM);
>
> offset = *offsetp;
>
> while (1) {
> + struct inode *dir_inode = file->f_path.dentry->d_inode;
> unsigned int reclen;
>
> cdp->err = nfserr_eof; /* will be cleared on successful read */
> @@ -1919,26 +1920,38 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
> if (!size)
> break;
>
> + /*
> + * Various filldir functions may end up calling back into
> + * lookup_one_len() and the file system's ->lookup() method.
> + * These expect i_mutex to be held, as it would within readdir.
> + */
> + host_err = mutex_lock_killable(&dir_inode->i_mutex);
> + if (host_err)
> + break;
> +
> de = (struct buffered_dirent *)buf.dirent;
> while (size > 0) {
> offset = de->offset;
>
> if (func(cdp, de->name, de->namlen, de->offset,
> de->ino, de->d_type))
> - goto done;
> + break;
>
> if (cdp->err != nfs_ok)
> - goto done;
> + break;
>
> reclen = ALIGN(sizeof(*de) + de->namlen,
> sizeof(u64));
> size -= reclen;
> de = (struct buffered_dirent *)((char *)de + reclen);
> }
> + mutex_unlock(&dir_inode->i_mutex);
> + if (size > 0) /* We bailed out early */
> + break;
> +
> offset = vfs_llseek(file, 0, SEEK_CUR);
> }
>
> - done:
> free_page((unsigned long)(buf.dirent));
>
> if (host_err)
>
> --
> dwmw2
>
next prev parent reply other threads:[~2009-04-19 20:52 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-03-19 14:54 Q: NFSD readdir in linux-2.6.28 hooanon05
2009-03-19 15:17 ` David Woodhouse
2009-03-19 15:34 ` hooanon05
2009-03-19 15:51 ` David Woodhouse
2009-04-17 9:32 ` David Woodhouse
2009-04-17 19:32 ` Al Viro
2009-04-17 22:17 ` David Woodhouse
2009-04-17 22:43 ` Al Viro
2009-04-17 22:51 ` David Woodhouse
2009-04-17 22:53 ` Al Viro
2009-04-17 22:55 ` David Woodhouse
2009-04-17 23:23 ` David Woodhouse
2009-04-17 23:37 ` Al Viro
2009-04-17 23:39 ` Al Viro
2009-04-18 0:15 ` [PATCH] Fix i_mutex handling in nfsd readdir David Woodhouse
2009-04-18 3:11 ` hooanon05
2009-04-18 14:25 ` Al Viro
2009-04-19 7:18 ` David Woodhouse
2009-04-19 12:27 ` [PATCH v2] " David Woodhouse
2009-04-19 20:51 ` J. Bruce Fields [this message]
2009-04-20 19:50 ` J. Bruce Fields
2009-04-21 0:29 ` Al Viro
2009-04-21 21:15 ` J. Bruce Fields
2009-04-21 21:54 ` Al Viro
2009-05-11 23:16 ` J. Bruce Fields
2009-04-22 4:41 ` hooanon05
2009-04-22 19:12 ` J. Bruce Fields
2009-04-23 6:40 ` hooanon05
2009-04-23 20:27 ` J. Bruce Fields
2009-05-05 23:35 ` J. Bruce Fields
2009-05-06 5:09 ` hooanon05
2009-05-06 20:20 ` J. Bruce Fields
2009-05-07 4:38 ` hooanon05
2009-05-08 18:47 ` J. Bruce Fields
2009-03-19 15:37 ` Q: NFSD readdir in linux-2.6.28 Matthew Wilcox
2009-03-19 15:45 ` hooanon05
2009-03-19 16:01 ` David Woodhouse
2009-04-16 21:45 ` J. Bruce Fields
2009-04-17 4:13 ` hooanon05
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090419205154.GA18110@fieldses.org \
--to=bfields@fieldses.org \
--cc=dwmw2@infradead.org \
--cc=hch@infradead.org \
--cc=hooanon05@yahoo.co.jp \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome