question about current code in async op
diff mbox

Message ID CAN-5tyEE3GnoUKGNiwiApKGpcP+5pj68nLAh9SLSTSJ=OuPcMw@mail.gmail.com
State New
Headers show

Commit Message

Olga Kornievskaia Feb. 10, 2017, 9:11 p.m. UTC
Hi folks,

As I was writing a new async operation I was looking at other async
functions for guidance (i.e.., nfs4_do_close()) and I noticed what
looks to me like a memory leak. Am I missing something?

I don’t see that “calldata” is freed if rpc_run_task() fails.

Thoughts?

Here’s what I have to fix it:

  status = rpc_wait_for_completion_task(task);
@@ -6023,6 +6025,7 @@ static struct rpc_task *nfs4_do_unlck(struct
file_lock *fl,
  .workqueue = nfsiod_workqueue,
  .flags = RPC_TASK_ASYNC,
  };
+ struct rpc_task *task;



  nfs4_state_protect(NFS_SERVER(lsp->ls_state->inode)->nfs_client,
  NFS_SP4_MACH_CRED_CLEANUP, &task_setup_data.rpc_client, &msg);
@@ -6042,7 +6045,10 @@ static struct rpc_task *nfs4_do_unlck(struct
file_lock *fl,
  msg.rpc_argp = &data->arg;
  msg.rpc_resp = &data->res;
  task_setup_data.callback_data = data;
- return rpc_run_task(&task_setup_data);
+ task = rpc_run_task(&task_setup_data);
+ if (IS_ERR(task))
+ kfree(data);
+ return task;
 }



 static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct
file_lock *request)
@@ -6311,8 +6317,10 @@ static int _nfs4_do_setlk(struct nfs4_state
*state, int cmd, struct file_lock *f
  } else
  data->arg.new_lock = 1;
  task = rpc_run_task(&task_setup_data);
- if (IS_ERR(task))
+ if (IS_ERR(task)) {
+ kfree(data);
  return PTR_ERR(task);
+ }
  ret = nfs4_wait_for_completion_rpc_task(task);
  if (ret == 0) {
  ret = data->rpc_status;
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Anna Schumaker Feb. 10, 2017, 9:20 p.m. UTC | #1
Hi Olga,

On 02/10/2017 04:11 PM, Olga Kornievskaia wrote:
> Hi folks,
> 
> As I was writing a new async operation I was looking at other async
> functions for guidance (i.e.., nfs4_do_close()) and I noticed what
> looks to me like a memory leak. Am I missing something?
> 
> I don’t see that “calldata” is freed if rpc_run_task() fails.

I think the current code is okay.  From what I can see, the only way rpc_run_task() can fail is if rpc_new_task() can't allocate the new task.  If this happens, then rpc_new_task() calls rpc_release_calldata() to free the calldata.

Am I missing something?

Anna

> 
> Thoughts?
> 
> Here’s what I have to fix it:
> 
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
> index d293f06..408cb5b 100644
> --- a/fs/nfs/nfs4proc.c
> +++ b/fs/nfs/nfs4proc.c
> @@ -3317,8 +3317,10 @@ int nfs4_do_close(struct nfs4_state *state,
> gfp_t gfp_mask, int wait)
>   msg.rpc_resp = &calldata->res;
>   task_setup_data.callback_data = calldata;
>   task = rpc_run_task(&task_setup_data);
> - if (IS_ERR(task))
> + if (IS_ERR(task)) {
> + kfree(calldata);
>   return PTR_ERR(task);

Isn't this handled by 

> + }
>   status = 0;
>   if (wait)
>   status = rpc_wait_for_completion_task(task);
> @@ -6023,6 +6025,7 @@ static struct rpc_task *nfs4_do_unlck(struct
> file_lock *fl,
>   .workqueue = nfsiod_workqueue,
>   .flags = RPC_TASK_ASYNC,
>   };
> + struct rpc_task *task;
> 
> 
> 
>   nfs4_state_protect(NFS_SERVER(lsp->ls_state->inode)->nfs_client,
>   NFS_SP4_MACH_CRED_CLEANUP, &task_setup_data.rpc_client, &msg);
> @@ -6042,7 +6045,10 @@ static struct rpc_task *nfs4_do_unlck(struct
> file_lock *fl,
>   msg.rpc_argp = &data->arg;
>   msg.rpc_resp = &data->res;
>   task_setup_data.callback_data = data;
> - return rpc_run_task(&task_setup_data);
> + task = rpc_run_task(&task_setup_data);
> + if (IS_ERR(task))
> + kfree(data);
> + return task;
>  }
> 
> 
> 
>  static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct
> file_lock *request)
> @@ -6311,8 +6317,10 @@ static int _nfs4_do_setlk(struct nfs4_state
> *state, int cmd, struct file_lock *f
>   } else
>   data->arg.new_lock = 1;
>   task = rpc_run_task(&task_setup_data);
> - if (IS_ERR(task))
> + if (IS_ERR(task)) {
> + kfree(data);
>   return PTR_ERR(task);
> + }
>   ret = nfs4_wait_for_completion_rpc_task(task);
>   if (ret == 0) {
>   ret = data->rpc_status;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Olga Kornievskaia Feb. 10, 2017, 9:49 p.m. UTC | #2
On Fri, Feb 10, 2017 at 4:20 PM, Anna Schumaker
<schumaker.anna@gmail.com> wrote:
> Hi Olga,
>
> On 02/10/2017 04:11 PM, Olga Kornievskaia wrote:
>> Hi folks,
>>
>> As I was writing a new async operation I was looking at other async
>> functions for guidance (i.e.., nfs4_do_close()) and I noticed what
>> looks to me like a memory leak. Am I missing something?
>>
>> I don’t see that “calldata” is freed if rpc_run_task() fails.
>
> I think the current code is okay.  From what I can see, the only way rpc_run_task() can fail is if rpc_new_task() can't allocate the new task.  If this happens, then rpc_new_task() calls rpc_release_calldata() to free the calldata.
>
> Am I missing something?

That's what I was missing :) Ok that makes sense. So release of the
calldata should be in the rpc_release function. Thank you!

>
> Anna
>
>>
>> Thoughts?
>>
>> Here’s what I have to fix it:
>>
>> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
>> diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
>> index d293f06..408cb5b 100644
>> --- a/fs/nfs/nfs4proc.c
>> +++ b/fs/nfs/nfs4proc.c
>> @@ -3317,8 +3317,10 @@ int nfs4_do_close(struct nfs4_state *state,
>> gfp_t gfp_mask, int wait)
>>   msg.rpc_resp = &calldata->res;
>>   task_setup_data.callback_data = calldata;
>>   task = rpc_run_task(&task_setup_data);
>> - if (IS_ERR(task))
>> + if (IS_ERR(task)) {
>> + kfree(calldata);
>>   return PTR_ERR(task);
>
> Isn't this handled by
>
>> + }
>>   status = 0;
>>   if (wait)
>>   status = rpc_wait_for_completion_task(task);
>> @@ -6023,6 +6025,7 @@ static struct rpc_task *nfs4_do_unlck(struct
>> file_lock *fl,
>>   .workqueue = nfsiod_workqueue,
>>   .flags = RPC_TASK_ASYNC,
>>   };
>> + struct rpc_task *task;
>>
>>
>>
>>   nfs4_state_protect(NFS_SERVER(lsp->ls_state->inode)->nfs_client,
>>   NFS_SP4_MACH_CRED_CLEANUP, &task_setup_data.rpc_client, &msg);
>> @@ -6042,7 +6045,10 @@ static struct rpc_task *nfs4_do_unlck(struct
>> file_lock *fl,
>>   msg.rpc_argp = &data->arg;
>>   msg.rpc_resp = &data->res;
>>   task_setup_data.callback_data = data;
>> - return rpc_run_task(&task_setup_data);
>> + task = rpc_run_task(&task_setup_data);
>> + if (IS_ERR(task))
>> + kfree(data);
>> + return task;
>>  }
>>
>>
>>
>>  static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct
>> file_lock *request)
>> @@ -6311,8 +6317,10 @@ static int _nfs4_do_setlk(struct nfs4_state
>> *state, int cmd, struct file_lock *f
>>   } else
>>   data->arg.new_lock = 1;
>>   task = rpc_run_task(&task_setup_data);
>> - if (IS_ERR(task))
>> + if (IS_ERR(task)) {
>> + kfree(data);
>>   return PTR_ERR(task);
>> + }
>>   ret = nfs4_wait_for_completion_rpc_task(task);
>>   if (ret == 0) {
>>   ret = data->rpc_status;
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index d293f06..408cb5b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3317,8 +3317,10 @@  int nfs4_do_close(struct nfs4_state *state,
gfp_t gfp_mask, int wait)
  msg.rpc_resp = &calldata->res;
  task_setup_data.callback_data = calldata;
  task = rpc_run_task(&task_setup_data);
- if (IS_ERR(task))
+ if (IS_ERR(task)) {
+ kfree(calldata);
  return PTR_ERR(task);
+ }
  status = 0;
  if (wait)