Re: [PATCH v3] ceph: fix writeback_count leak in write_folio_nounlock()

From: Viacheslav Dubeyko

Date: Wed Jun 03 2026 - 13:00:55 EST


On Wed, 2026-06-03 at 01:25 +0000, Wentao Liang wrote:
> write_folio_nounlock() increments fsc->writeback_count to track
> in-flight writeback operations. On several error paths where the
> function returns early (folio lookup failure, snapshot context
> allocation failure, and writepages submission failure), the function
> returns without calling atomic_long_dec_return() to decrement the
> counter.
>
> Each leaked increment keeps the counter above zero, which can prevent
> the filesystem from cleanly unmounting or suspending writes.
>
> Add atomic_long_dec_return() calls on all error paths that currently
> return without decrementing the counter.
>
> Fixes: d55207717ded ("ceph: add encryption support to writepage and
> writepages")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Wentao Liang <vulab@xxxxxxxxxxx>
>
> ---
> Changes in v3:
> - Also clear write_congested flag when decrementing writeback_count
>   on error paths, as suggested by Viacheslav Dubeyko.
> - Fix typo error.
> ---
>  fs/ceph/addr.c | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
> index a606378649c3..7fab73874068 100644
> --- a/fs/ceph/addr.c
> +++ b/fs/ceph/addr.c
> @@ -790,7 +790,9 @@ static int write_folio_nounlock(struct folio
> *folio,
>       ceph_wbc.truncate_size, true);
>   if (IS_ERR(req)) {
>   folio_redirty_for_writepage(wbc, folio);
> - atomic_long_dec(&fsc->writeback_count);

Patch is not correctly prepared. Because, current state of the code
hasn't atomic_long_dec().

Thanks,
Slava.

> + if (atomic_long_dec_return(&fsc->writeback_count) <
> + CONGESTION_OFF_THRESH(fsc-
> >mount_options->congestion_kb))
> + fsc->write_congested = false;
>   return PTR_ERR(req);
>   }
>  
> @@ -810,7 +812,9 @@ static int write_folio_nounlock(struct folio
> *folio,
>   folio_redirty_for_writepage(wbc, folio);
>   folio_end_writeback(folio);
>   ceph_osdc_put_request(req);
> - atomic_long_dec(&fsc->writeback_count);
> + if (atomic_long_dec_return(&fsc-
> >writeback_count) <
> + CONGESTION_OFF_THRESH(fsc-
> >mount_options->congestion_kb))
> + fsc->write_congested = false;
>   return PTR_ERR(bounce_page);
>   }
>   }
> @@ -849,7 +853,9 @@ static int write_folio_nounlock(struct folio
> *folio,
>         ceph_vinop(inode), folio);
>   folio_redirty_for_writepage(wbc, folio);
>   folio_end_writeback(folio);
> - atomic_long_dec_return(&fsc-
> >writeback_count);
> + if (atomic_long_dec_return(&fsc-
> >writeback_count) <
> + CONGESTION_OFF_THRESH(fsc-
> >mount_options->congestion_kb))
> + fsc->write_congested = false;
>   return err;
>   }
>   if (err == -EBLOCKLISTED)