Message ID | 20200513115330.5187-1-adam@forsedomani.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | cifs: fix leaked reference on requeued write | expand |
Shyam and Pavel noticed things which didn't make sense e.g. in cifs_writepages weare putting the reference unconditionally but in cifs_write_from_iter we are doing the same thing. So how was this working before - should have resulted in a reference leak and direct i/o shouldn't have had a chance to complete?? and wouldn't there be an underrun if a retryable error with your patch with it getting called twice? Jeff, Any thoughts on this? On Wed, May 13, 2020 at 6:55 AM Adam McCoy <adam@forsedomani.com> wrote: > > Failed async writes that are requeued may not clean up a refcount > on the file, which can result in a leaked open. This scenario arises > very reliably when using persistent handles and a reconnect occurs > while writing. > > cifs_writev_requeue only releases the reference if the write fails > (rc != 0). The server->ops->async_writev operation will take its own > reference, so the initial reference can always be released. > > Signed-off-by: Adam McCoy <adam@forsedomani.com> > --- > fs/cifs/cifssmb.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > index 182b864b3075..5014a82391ff 100644 > --- a/fs/cifs/cifssmb.c > +++ b/fs/cifs/cifssmb.c > @@ -2152,8 +2152,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata) > } > } > > + kref_put(&wdata2->refcount, cifs_writedata_release); > if (rc) { > - kref_put(&wdata2->refcount, cifs_writedata_release); > if (is_retryable_error(rc)) > continue; > i += nr_pages; > -- > 2.17.1 > -- Thanks, Steve
Part of this makes sense Pavel reminded me: in cifs_writepages() we don't need to reference wdata because we are leaving the function. in cifs_write_from_iter() we put all wdatas in the list and that's why we need an extra reference there On Wed, May 13, 2020 at 2:14 PM Steve French <smfrench@gmail.com> wrote: > > Shyam and Pavel noticed things which didn't make sense > > e.g. in cifs_writepages weare putting the reference unconditionally > but in cifs_write_from_iter we are doing the same thing. So how was > this working before - should have resulted in a reference leak and > direct i/o shouldn't have had a chance to complete?? > > and wouldn't there be an underrun if a retryable error with your patch > with it getting called twice? > > Jeff, > Any thoughts on this? > > > > On Wed, May 13, 2020 at 6:55 AM Adam McCoy <adam@forsedomani.com> wrote: > > > > Failed async writes that are requeued may not clean up a refcount > > on the file, which can result in a leaked open. This scenario arises > > very reliably when using persistent handles and a reconnect occurs > > while writing. > > > > cifs_writev_requeue only releases the reference if the write fails > > (rc != 0). The server->ops->async_writev operation will take its own > > reference, so the initial reference can always be released. > > > > Signed-off-by: Adam McCoy <adam@forsedomani.com> > > --- > > fs/cifs/cifssmb.c | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > > index 182b864b3075..5014a82391ff 100644 > > --- a/fs/cifs/cifssmb.c > > +++ b/fs/cifs/cifssmb.c > > @@ -2152,8 +2152,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata) > > } > > } > > > > + kref_put(&wdata2->refcount, cifs_writedata_release); > > if (rc) { > > - kref_put(&wdata2->refcount, cifs_writedata_release); > > if (is_retryable_error(rc)) > > continue; > > i += nr_pages; > > -- > > 2.17.1 > > > > > -- > Thanks, > > Steve
> Part of this makes sense Pavel reminded me: > in cifs_writepages() we don't need to reference wdata because we > are leaving the function. in cifs_write_from_iter() we put all wdatas > in the list and that's why we need an extra reference there Yes, this looks right. cifs_writev_requeue() seems to work like cifs_writepages() in that the wdata2 reference disappears when the loop exits. If the loop iterates a new struct is created each time. > and wouldn't there be an underrun if a retryable error with your patch > with it getting called twice? There shouldn't be any difference if there is any kind of write error (rc != 0), since the put call is just moving. The only difference should be that the put call will happen if the write succeeds. On Wed, May 13, 2020 at 04:04:08PM -0500, Steve French wrote: > Part of this makes sense Pavel reminded me: > in cifs_writepages() we don't need to reference wdata because we > are leaving the function. in cifs_write_from_iter() we put all wdatas > in the list and that's why we need an extra reference there > > > On Wed, May 13, 2020 at 2:14 PM Steve French <smfrench@gmail.com> wrote: > > > > Shyam and Pavel noticed things which didn't make sense > > > > e.g. in cifs_writepages weare putting the reference unconditionally > > but in cifs_write_from_iter we are doing the same thing. So how was > > this working before - should have resulted in a reference leak and > > direct i/o shouldn't have had a chance to complete?? > > > > and wouldn't there be an underrun if a retryable error with your patch > > with it getting called twice? > > > > Jeff, > > Any thoughts on this? > > > > > > > > On Wed, May 13, 2020 at 6:55 AM Adam McCoy <adam@forsedomani.com> wrote: > > > > > > Failed async writes that are requeued may not clean up a refcount > > > on the file, which can result in a leaked open. This scenario arises > > > very reliably when using persistent handles and a reconnect occurs > > > while writing. > > > > > > cifs_writev_requeue only releases the reference if the write fails > > > (rc != 0). The server->ops->async_writev operation will take its own > > > reference, so the initial reference can always be released. > > > > > > Signed-off-by: Adam McCoy <adam@forsedomani.com> > > > --- > > > fs/cifs/cifssmb.c | 2 +- > > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > > > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c > > > index 182b864b3075..5014a82391ff 100644 > > > --- a/fs/cifs/cifssmb.c > > > +++ b/fs/cifs/cifssmb.c > > > @@ -2152,8 +2152,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata) > > > } > > > } > > > > > > + kref_put(&wdata2->refcount, cifs_writedata_release); > > > if (rc) { > > > - kref_put(&wdata2->refcount, cifs_writedata_release); > > > if (is_retryable_error(rc)) > > > continue; > > > i += nr_pages; > > > -- > > > 2.17.1 > > > > > > > > > -- > > Thanks, > > > > Steve > > > > -- > Thanks, > > Steve
ср, 13 мая 2020 г. в 18:18, Adam McCoy <adam@forsedomani.com>: > > > Part of this makes sense Pavel reminded me: > > in cifs_writepages() we don't need to reference wdata because we > > are leaving the function. in cifs_write_from_iter() we put all wdatas > > in the list and that's why we need an extra reference there > > Yes, this looks right. cifs_writev_requeue() seems to work like > cifs_writepages() in that the wdata2 reference disappears when the loop > exits. If the loop iterates a new struct is created each time. > > > and wouldn't there be an underrun if a retryable error with your patch > > with it getting called twice? > > There shouldn't be any difference if there is any kind of write error > (rc != 0), since the put call is just moving. The only difference > should be that the put call will happen if the write succeeds. > Thanks for the patch! Good catch! Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> This is the old code and the problem is important to fix, so, stable tag is needed. -- Best regards, Pavel Shilovsky
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 182b864b3075..5014a82391ff 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -2152,8 +2152,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata) } } + kref_put(&wdata2->refcount, cifs_writedata_release); if (rc) { - kref_put(&wdata2->refcount, cifs_writedata_release); if (is_retryable_error(rc)) continue; i += nr_pages;
Failed async writes that are requeued may not clean up a refcount on the file, which can result in a leaked open. This scenario arises very reliably when using persistent handles and a reconnect occurs while writing. cifs_writev_requeue only releases the reference if the write fails (rc != 0). The server->ops->async_writev operation will take its own reference, so the initial reference can always be released. Signed-off-by: Adam McCoy <adam@forsedomani.com> --- fs/cifs/cifssmb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)