Commit 6cc04054 authored by Vivek Kasireddy's avatar Vivek Kasireddy Committed by Andrew Morton

mm/gup: introduce unpin_folio/unpin_folios helpers

Patch series "mm/gup: Introduce memfd_pin_folios() for pinning memfd
folios", v16.

Currently, some drivers (e.g, Udmabuf) that want to longterm-pin the
pages/folios associated with a memfd, do so by simply taking a reference
on them.  This is not desirable because the pages/folios may reside in
Movable zone or CMA block.

Therefore, having drivers use memfd_pin_folios() API ensures that the
folios are appropriately pinned via FOLL_PIN for longterm DMA.

This patchset also introduces a few helpers and converts the Udmabuf
driver to use folios and memfd_pin_folios() API to longterm-pin the folios
for DMA.  Two new Udmabuf selftests are also included to test the driver
and the new API.


This patch (of 9):

These helpers are the folio versions of unpin_user_page/unpin_user_pages. 
They are currently only useful for unpinning folios pinned by
memfd_pin_folios() or other associated routines.  However, they could find
new uses in the future, when more and more folio-only helpers are added to
GUP.

We should probably sanity check the folio as part of unpin similar to how
it is done in unpin_user_page/unpin_user_pages but we cannot cleanly do
that at the moment without also checking the subpage.  Therefore, sanity
checking needs to be added to these routines once we have a way to
determine if any given folio is anon-exclusive (via a per folio
AnonExclusive flag).

Link: https://lkml.kernel.org/r/20240624063952.1572359-1-vivek.kasireddy@intel.com
Link: https://lkml.kernel.org/r/20240624063952.1572359-2-vivek.kasireddy@intel.comSigned-off-by: default avatarVivek Kasireddy <vivek.kasireddy@intel.com>
Suggested-by: default avatarDavid Hildenbrand <david@redhat.com>
Reviewed-by: default avatarDavid Hildenbrand <david@redhat.com>
Acked-by: default avatarDave Airlie <airlied@redhat.com>
Acked-by: default avatarGerd Hoffmann <kraxel@redhat.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Peter Xu <peterx@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Dongwon Kim <dongwon.kim@intel.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Junxiao Chang <junxiao.chang@intel.com>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Mike Kravetz <mike.kravetz@oracle.com>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 8edc9c4e
...@@ -1584,11 +1584,13 @@ static inline void put_page(struct page *page) ...@@ -1584,11 +1584,13 @@ static inline void put_page(struct page *page)
#define GUP_PIN_COUNTING_BIAS (1U << 10) #define GUP_PIN_COUNTING_BIAS (1U << 10)
void unpin_user_page(struct page *page); void unpin_user_page(struct page *page);
void unpin_folio(struct folio *folio);
void unpin_user_pages_dirty_lock(struct page **pages, unsigned long npages, void unpin_user_pages_dirty_lock(struct page **pages, unsigned long npages,
bool make_dirty); bool make_dirty);
void unpin_user_page_range_dirty_lock(struct page *page, unsigned long npages, void unpin_user_page_range_dirty_lock(struct page *page, unsigned long npages,
bool make_dirty); bool make_dirty);
void unpin_user_pages(struct page **pages, unsigned long npages); void unpin_user_pages(struct page **pages, unsigned long npages);
void unpin_folios(struct folio **folios, unsigned long nfolios);
static inline bool is_cow_mapping(vm_flags_t flags) static inline bool is_cow_mapping(vm_flags_t flags)
{ {
......
...@@ -188,6 +188,19 @@ void unpin_user_page(struct page *page) ...@@ -188,6 +188,19 @@ void unpin_user_page(struct page *page)
} }
EXPORT_SYMBOL(unpin_user_page); EXPORT_SYMBOL(unpin_user_page);
/**
* unpin_folio() - release a dma-pinned folio
* @folio: pointer to folio to be released
*
* Folios that were pinned via memfd_pin_folios() or other similar routines
* must be released either using unpin_folio() or unpin_folios().
*/
void unpin_folio(struct folio *folio)
{
gup_put_folio(folio, 1, FOLL_PIN);
}
EXPORT_SYMBOL_GPL(unpin_folio);
/** /**
* folio_add_pin - Try to get an additional pin on a pinned folio * folio_add_pin - Try to get an additional pin on a pinned folio
* @folio: The folio to be pinned * @folio: The folio to be pinned
...@@ -400,6 +413,40 @@ void unpin_user_pages(struct page **pages, unsigned long npages) ...@@ -400,6 +413,40 @@ void unpin_user_pages(struct page **pages, unsigned long npages)
} }
EXPORT_SYMBOL(unpin_user_pages); EXPORT_SYMBOL(unpin_user_pages);
/**
* unpin_folios() - release an array of gup-pinned folios.
* @folios: array of folios to be marked dirty and released.
* @nfolios: number of folios in the @folios array.
*
* For each folio in the @folios array, release the folio using gup_put_folio.
*
* Please see the unpin_folio() documentation for details.
*/
void unpin_folios(struct folio **folios, unsigned long nfolios)
{
unsigned long i = 0, j;
/*
* If this WARN_ON() fires, then the system *might* be leaking folios
* (by leaving them pinned), but probably not. More likely, gup/pup
* returned a hard -ERRNO error to the caller, who erroneously passed
* it here.
*/
if (WARN_ON(IS_ERR_VALUE(nfolios)))
return;
while (i < nfolios) {
for (j = i + 1; j < nfolios; j++)
if (folios[i] != folios[j])
break;
if (folios[i])
gup_put_folio(folios[i], j - i, FOLL_PIN);
i = j;
}
}
EXPORT_SYMBOL_GPL(unpin_folios);
/* /*
* Set the MMF_HAS_PINNED if not set yet; after set it'll be there for the mm's * Set the MMF_HAS_PINNED if not set yet; after set it'll be there for the mm's
* lifecycle. Avoid setting the bit unless necessary, or it might cause write * lifecycle. Avoid setting the bit unless necessary, or it might cause write
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment