Linux NFS development
 help / color / mirror / Atom feed
From: Benny Halevy <bhalevy@panasas.com>
To: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: linux-nfs@vger.kernel.org
Subject: Re: [PATCH] nfs: handle nfs_{read,write,commit}_rpcsetup errors
Date: Tue, 15 Apr 2008 10:09:50 +0300	[thread overview]
Message-ID: <480454BE.30500@panasas.com> (raw)
In-Reply-To: <1208195681.11223.18.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>

On Apr. 14, 2008, 20:54 +0300, Trond Myklebust <Trond.Myklebust@netapp.com> wrote:
> On Mon, 2008-04-14 at 20:31 +0300, Benny Halevy wrote:
>> Currently, nfs_{read,write,commit}_rpcsetup return no errors.
>> All three call rpc_run_task that can fail when out of memory.
>> Ignoring these failures leads to hangs.
> 
> How?

I've seen this with instrumentation I've put in the rpc_run_task path.
However, I reexamined the call sites and I agree that since we pass
both task_setup_data.task and task_setup_data.rpc_message.rpc_cred
rpc_run_task will never fail in the current implementation.

How about adding the following BUG()s instead?

Benny

diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 5a70be5..9db9db1 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -206,6 +206,8 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
 	task = rpc_run_task(&task_setup_data);
 	if (!IS_ERR(task))
 		rpc_put_task(task);
+	else
+		BUG();
 }
 
 static void
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index bed6341..06e6363 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -849,6 +849,8 @@ static void nfs_write_rpcsetup(struct nfs_page *req,
 	task = rpc_run_task(&task_setup_data);
 	if (!IS_ERR(task))
 		rpc_put_task(task);
+	else
+		BUG();
 }
 
 /*
@@ -1216,6 +1218,8 @@ static void nfs_commit_rpcsetup(struct list_head *head,
 	task = rpc_run_task(&task_setup_data);
 	if (!IS_ERR(task))
 		rpc_put_task(task);
+	else
+		BUG();
 }
 
 /*

> 
>> Signed-off-by: Benny Halevy <bhalevy@panasas.com>
>> ---
>>  fs/nfs/read.c  |   35 +++++++++++++++++++++--------
>>  fs/nfs/write.c |   66 ++++++++++++++++++++++++++++++++++++-------------------
>>  2 files changed, 68 insertions(+), 33 deletions(-)
>>
>> diff --git a/fs/nfs/read.c b/fs/nfs/read.c
>> index 5a70be5..85df148 100644
>> --- a/fs/nfs/read.c
>> +++ b/fs/nfs/read.c
>> @@ -156,7 +156,7 @@ static void nfs_readpage_release(struct nfs_page *req)
>>  /*
>>   * Set up the NFS read request struct
>>   */
>> -static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
>> +static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
>>  		const struct rpc_call_ops *call_ops,
>>  		unsigned int count, unsigned int offset)
>>  {
>> @@ -204,8 +204,10 @@ static void nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data,
>>  			(unsigned long long)data->args.offset);
>>  
>>  	task = rpc_run_task(&task_setup_data);
>> -	if (!IS_ERR(task))
>> -		rpc_put_task(task);
>> +	if (unlikely(IS_ERR(task)))
>> +		return PTR_ERR(task);
>> +	rpc_put_task(task);
>> +	return 0;
>>  }
>>  
>>  static void
>> @@ -242,6 +244,7 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
>>  	size_t rsize = NFS_SERVER(inode)->rsize, nbytes;
>>  	unsigned int offset;
>>  	int requests = 0;
>> +	int ret = -ENOMEM;
>>  	LIST_HEAD(list);
>>  
>>  	nfs_list_remove_request(req);
>> @@ -271,8 +274,12 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigne
>>  
>>  		if (nbytes < rsize)
>>  			rsize = nbytes;
>> -		nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
>> -				  rsize, offset);
>> +		ret = nfs_read_rpcsetup(req, data, &nfs_read_partial_ops,
>> +					rsize, offset);
>> +		if (unlikely(ret)) {
>> +			list_add(&data->pages, &list);
> 
> NACK. This is a use-after-free case.
> 
> The call to rpc_run_task() is _guaranteed_ to always call
> nfs_readpage_release(). You therefore no longer hold the page lock, nor
> can you rely on 'data' still being around.
> 
> The same applies to all the other "fixes".

Agreed. I missed that.

Benny

> 
> Trond
> 


      parent reply	other threads:[~2008-04-15  7:10 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-04-14 17:31 [PATCH] nfs: handle nfs_{read,write,commit}_rpcsetup errors Benny Halevy
2008-04-14 17:54 ` Trond Myklebust
     [not found]   ` <1208195681.11223.18.camel-rJ7iovZKK19ZJLDQqaL3InhyD016LWXt@public.gmane.org>
2008-04-15  7:09     ` Benny Halevy [this message]

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=480454BE.30500@panasas.com \
    --to=bhalevy@panasas.com \
    --cc=Trond.Myklebust@netapp.com \
    --cc=linux-nfs@vger.kernel.org \
    /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