lkml.org 
[lkml]   [2022]   [Aug]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.18 11/88] mm: fix missing wake-up event for FSDAX pages
    Date
    From: Muchun Song <songmuchun@bytedance.com>

    commit f4f451a16dd1f478fdb966bcbb612c1e4ce6b962 upstream.

    FSDAX page refcounts are 1-based, rather than 0-based: if refcount is
    1, then the page is freed. The FSDAX pages can be pinned through GUP,
    then they will be unpinned via unpin_user_page() using a folio variant
    to put the page, however, folio variants did not consider this special
    case, the result will be to miss a wakeup event (like the user of
    __fuse_dax_break_layouts()). This results in a task being permanently
    stuck in TASK_INTERRUPTIBLE state.

    Since FSDAX pages are only possibly obtained by GUP users, so fix GUP
    instead of folio_put() to lower overhead.

    Link: https://lkml.kernel.org/r/20220705123532.283-1-songmuchun@bytedance.com
    Fixes: d8ddc099c6b3 ("mm/gup: Add gup_put_folio()")
    Signed-off-by: Muchun Song <songmuchun@bytedance.com>
    Suggested-by: Matthew Wilcox <willy@infradead.org>
    Cc: Jason Gunthorpe <jgg@ziepe.ca>
    Cc: John Hubbard <jhubbard@nvidia.com>
    Cc: William Kucharski <william.kucharski@oracle.com>
    Cc: Dan Williams <dan.j.williams@intel.com>
    Cc: Jan Kara <jack@suse.cz>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    include/linux/mm.h | 14 +++++++++-----
    mm/gup.c | 6 ++++--
    mm/memremap.c | 6 +++---
    3 files changed, 16 insertions(+), 10 deletions(-)

    --- a/include/linux/mm.h
    +++ b/include/linux/mm.h
    @@ -1130,23 +1130,27 @@ static inline bool is_zone_movable_page(
    #if defined(CONFIG_ZONE_DEVICE) && defined(CONFIG_FS_DAX)
    DECLARE_STATIC_KEY_FALSE(devmap_managed_key);

    -bool __put_devmap_managed_page(struct page *page);
    -static inline bool put_devmap_managed_page(struct page *page)
    +bool __put_devmap_managed_page_refs(struct page *page, int refs);
    +static inline bool put_devmap_managed_page_refs(struct page *page, int refs)
    {
    if (!static_branch_unlikely(&devmap_managed_key))
    return false;
    if (!is_zone_device_page(page))
    return false;
    - return __put_devmap_managed_page(page);
    + return __put_devmap_managed_page_refs(page, refs);
    }
    -
    #else /* CONFIG_ZONE_DEVICE && CONFIG_FS_DAX */
    -static inline bool put_devmap_managed_page(struct page *page)
    +static inline bool put_devmap_managed_page_refs(struct page *page, int refs)
    {
    return false;
    }
    #endif /* CONFIG_ZONE_DEVICE && CONFIG_FS_DAX */

    +static inline bool put_devmap_managed_page(struct page *page)
    +{
    + return put_devmap_managed_page_refs(page, 1);
    +}
    +
    /* 127: arbitrary random number, small enough to assemble well */
    #define folio_ref_zero_or_close_to_overflow(folio) \
    ((unsigned int) folio_ref_count(folio) + 127u <= 127u)
    --- a/mm/gup.c
    +++ b/mm/gup.c
    @@ -54,7 +54,8 @@ retry:
    * belongs to this folio.
    */
    if (unlikely(page_folio(page) != folio)) {
    - folio_put_refs(folio, refs);
    + if (!put_devmap_managed_page_refs(&folio->page, refs))
    + folio_put_refs(folio, refs);
    goto retry;
    }

    @@ -143,7 +144,8 @@ static void gup_put_folio(struct folio *
    refs *= GUP_PIN_COUNTING_BIAS;
    }

    - folio_put_refs(folio, refs);
    + if (!put_devmap_managed_page_refs(&folio->page, refs))
    + folio_put_refs(folio, refs);
    }

    /**
    --- a/mm/memremap.c
    +++ b/mm/memremap.c
    @@ -489,7 +489,7 @@ void free_zone_device_page(struct page *
    }

    #ifdef CONFIG_FS_DAX
    -bool __put_devmap_managed_page(struct page *page)
    +bool __put_devmap_managed_page_refs(struct page *page, int refs)
    {
    if (page->pgmap->type != MEMORY_DEVICE_FS_DAX)
    return false;
    @@ -499,9 +499,9 @@ bool __put_devmap_managed_page(struct pa
    * refcount is 1, then the page is free and the refcount is
    * stable because nobody holds a reference on the page.
    */
    - if (page_ref_dec_return(page) == 1)
    + if (page_ref_sub_return(page, refs) == 1)
    wake_up_var(&page->_refcount);
    return true;
    }
    -EXPORT_SYMBOL(__put_devmap_managed_page);
    +EXPORT_SYMBOL(__put_devmap_managed_page_refs);
    #endif /* CONFIG_FS_DAX */

    \
     
     \ /
      Last update: 2022-08-01 14:10    [W:4.125 / U:0.056 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site