mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 15:12:13 +02:00
iommu/pages: Make iommu_put_pages_list() work with high order allocations
alloc_pages_node(, order) needs to be paired with __free_pages(, order) to
free all the allocated pages. For order != 0 the return from
alloc_pages_node() is just a page list, it hasn't been formed into a
folio.
However iommu_put_pages_list() just calls put_page() on the head page of
an allocation, which will end up leaking the tail pages if order != 0.
Fix this by using __GFP_COMP to create a high order folio and then always
use put_page() to free the full high order folio.
__iommu_free_account() can get the order of the allocation via
folio_order(), which corrects the accounting of high order allocations in
iommu_put_pages_list(). This is the same technique slub uses.
As far as I can tell, none of the places using high order allocations are
also using the free list, so this not a current bug.
Fixes: 06c375053c ("iommu/vt-d: add wrapper functions for page allocations")
Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com>
Tested-by: Nicolin Chen <nicolinc@nvidia.com>
Tested-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/4-v4-c8663abbb606+3f7-iommu_pages_jgg@nvidia.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
8360c03dd9
commit
c11a1a4792
|
|
@ -38,8 +38,9 @@ static inline void __iommu_alloc_account(struct page *page, int order)
|
|||
* @page: head struct page of the page.
|
||||
* @order: order of the page
|
||||
*/
|
||||
static inline void __iommu_free_account(struct page *page, int order)
|
||||
static inline void __iommu_free_account(struct page *page)
|
||||
{
|
||||
unsigned int order = folio_order(page_folio(page));
|
||||
const long pgcnt = 1l << order;
|
||||
|
||||
mod_node_page_state(page_pgdat(page), NR_IOMMU_PAGES, -pgcnt);
|
||||
|
|
@ -57,7 +58,8 @@ static inline void __iommu_free_account(struct page *page, int order)
|
|||
*/
|
||||
static inline void *iommu_alloc_pages_node(int nid, gfp_t gfp, int order)
|
||||
{
|
||||
struct page *page = alloc_pages_node(nid, gfp | __GFP_ZERO, order);
|
||||
struct page *page =
|
||||
alloc_pages_node(nid, gfp | __GFP_ZERO | __GFP_COMP, order);
|
||||
|
||||
if (unlikely(!page))
|
||||
return NULL;
|
||||
|
|
@ -115,8 +117,8 @@ static inline void iommu_free_pages(void *virt, int order)
|
|||
return;
|
||||
|
||||
page = virt_to_page(virt);
|
||||
__iommu_free_account(page, order);
|
||||
__free_pages(page, order);
|
||||
__iommu_free_account(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +145,7 @@ static inline void iommu_put_pages_list(struct list_head *page)
|
|||
struct page *p = list_entry(page->prev, struct page, lru);
|
||||
|
||||
list_del(&p->lru);
|
||||
__iommu_free_account(p, 0);
|
||||
__iommu_free_account(p);
|
||||
put_page(p);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user