From: Oleksandr Tyshchenko <olekstysh@gmail.com>
To: xen-devel@lists.xenproject.org
Cc: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>,
Julien Grall <julien.grall@arm.com>,
Stefano Stabellini <sstabellini@kernel.org>
Subject: [RFC PATCH v1 6/7] iommu/arm: ipmmu-vmsa: Deallocate page table asynchronously
Date: Wed, 26 Jul 2017 18:10:03 +0300 [thread overview]
Message-ID: <1501081804-4882-7-git-send-email-olekstysh@gmail.com> (raw)
In-Reply-To: <1501081804-4882-1-git-send-email-olekstysh@gmail.com>
From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
This is the PoC how to optimize page table deallocation sequence
by splitting it into separate chunks.
Use iommu_pt_cleanup_list to queue pages that need to be handled and
freed next time. Use free_page_table platform callback to dequeue
pages.
Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
CC: Julien Grall <julien.grall@arm.com>
CC: Stefano Stabellini <sstabellini@kernel.org>
---
xen/drivers/passthrough/arm/io-pgtable-arm.c | 94 +++++++++++++++++++++++++---
xen/drivers/passthrough/arm/io-pgtable.c | 5 +-
xen/drivers/passthrough/arm/io-pgtable.h | 4 +-
xen/drivers/passthrough/arm/ipmmu-vmsa.c | 33 ++++++++--
4 files changed, 119 insertions(+), 17 deletions(-)
diff --git a/xen/drivers/passthrough/arm/io-pgtable-arm.c b/xen/drivers/passthrough/arm/io-pgtable-arm.c
index c98caa3..7673fda 100644
--- a/xen/drivers/passthrough/arm/io-pgtable-arm.c
+++ b/xen/drivers/passthrough/arm/io-pgtable-arm.c
@@ -254,6 +254,10 @@ struct arm_lpae_io_pgtable {
/* Xen: We deal with domain pages. */
struct page_info *pgd;
+ /* Xen: To indicate that deallocation sequence is in progress. */
+ bool_t cleanup;
+ /* Xen: To count allocated domain pages. */
+ unsigned int page_count;
};
typedef u64 arm_lpae_iopte;
@@ -329,7 +333,7 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
#endif
static struct page_info *__arm_lpae_alloc_pages(size_t size, gfp_t gfp,
- struct io_pgtable_cfg *cfg)
+ struct arm_lpae_io_pgtable *data)
{
struct page_info *pages;
unsigned int order = get_order_from_bytes(size);
@@ -342,15 +346,21 @@ static struct page_info *__arm_lpae_alloc_pages(size_t size, gfp_t gfp,
for (i = 0; i < (1 << order); i ++)
clear_and_clean_page(pages + i);
+ data->page_count += (1<<order);
+
return pages;
}
static void __arm_lpae_free_pages(struct page_info *pages, size_t size,
- struct io_pgtable_cfg *cfg)
+ struct arm_lpae_io_pgtable *data)
{
unsigned int order = get_order_from_bytes(size);
+ BUG_ON((int)data->page_count <= 0);
+
free_domheap_pages(pages, order);
+
+ data->page_count -= (1<<order);
}
static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
@@ -434,7 +444,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
pte = *ptep;
if (!pte) {
page = __arm_lpae_alloc_pages(ARM_LPAE_GRANULE(data),
- GFP_ATOMIC, cfg);
+ GFP_ATOMIC, data);
if (!page)
return -ENOMEM;
@@ -526,6 +536,46 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,
return ret;
}
+static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
+ struct page_info *page);
+
+/*
+ * TODO: We have reused unused at the moment "page->pad" variable for
+ * storing "data" pointer we need during deallocation sequence. The current
+ * free_page_table platform callback carries the only one "page" argument.
+ * To perform required calculations with the current (generic) allocator
+ * implementation we are highly interested in the following fields:
+ * - data->levels
+ * - data->pg_shift
+ * - data->pgd_size
+ * But, this necessity might be avoided if we integrate allocator code with
+ * IPMMU-VMSA driver. And these variables will turn into the
+ * corresponding #define-s.
+ */
+static void __arm_lpae_free_next_pgtable(struct arm_lpae_io_pgtable *data,
+ int lvl, struct page_info *page)
+{
+ if (!data->cleanup) {
+ /*
+ * We are here during normal page table maintenance. Just call
+ * __arm_lpae_free_pgtable(), what we actually had to call.
+ */
+ __arm_lpae_free_pgtable(data, lvl, page);
+ } else {
+ /*
+ * The page table deallocation sequence is in progress. Use some fields
+ * in struct page_info to pass arguments we will need during handling
+ * this page back. Queue page to list.
+ */
+ PFN_ORDER(page) = lvl;
+ page->pad = (u64)&data->iop.ops;
+
+ spin_lock(&iommu_pt_cleanup_lock);
+ page_list_add_tail(page, &iommu_pt_cleanup_list);
+ spin_unlock(&iommu_pt_cleanup_lock);
+ }
+}
+
/* Xen: We deal with domain pages. */
static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
struct page_info *page)
@@ -553,19 +603,41 @@ static void __arm_lpae_free_pgtable(struct arm_lpae_io_pgtable *data, int lvl,
if (!pte || iopte_leaf(pte, lvl))
continue;
- __arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
+ __arm_lpae_free_next_pgtable(data, lvl + 1, iopte_deref(pte, data));
}
unmap_domain_page(start);
- __arm_lpae_free_pages(page, table_size, &data->iop.cfg);
+ __arm_lpae_free_pages(page, table_size, data);
}
-static void arm_lpae_free_pgtable(struct io_pgtable *iop)
+/*
+ * We added extra "page" argument since we want to know what page is processed
+ * at the moment and should be freed.
+ * */
+static void arm_lpae_free_pgtable(struct io_pgtable *iop, struct page_info *page)
{
struct arm_lpae_io_pgtable *data = io_pgtable_to_data(iop);
+ int lvl;
- __arm_lpae_free_pgtable(data, ARM_LPAE_START_LVL(data), data->pgd);
- kfree(data);
+ if (!data->cleanup) {
+ /* Start page table deallocation sequence from the first level. */
+ data->cleanup = true;
+ lvl = ARM_LPAE_START_LVL(data);
+ } else {
+ /* Retrieve the level to continue deallocation sequence from. */
+ lvl = PFN_ORDER(page);
+ PFN_ORDER(page) = 0;
+ page->pad = 0;
+ }
+
+ __arm_lpae_free_pgtable(data, lvl, page);
+
+ /*
+ * Seems, we have already deallocated all pages, so it is time
+ * to release unfreed resource.
+ */
+ if (!data->page_count)
+ kfree(data);
}
/* Xen: We deal with domain pages. */
@@ -889,8 +961,12 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
cfg->arm_lpae_s1_cfg.mair[0] = reg;
cfg->arm_lpae_s1_cfg.mair[1] = 0;
+ /* Just to be sure */
+ data->cleanup = false;
+ data->page_count = 0;
+
/* Looking good; allocate a pgd */
- data->pgd = __arm_lpae_alloc_pages(data->pgd_size, GFP_KERNEL, cfg);
+ data->pgd = __arm_lpae_alloc_pages(data->pgd_size, GFP_KERNEL, data);
if (!data->pgd)
goto out_free_data;
diff --git a/xen/drivers/passthrough/arm/io-pgtable.c b/xen/drivers/passthrough/arm/io-pgtable.c
index bfc7020..e25d731 100644
--- a/xen/drivers/passthrough/arm/io-pgtable.c
+++ b/xen/drivers/passthrough/arm/io-pgtable.c
@@ -77,7 +77,7 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
* It is the IOMMU driver's responsibility to ensure that the page table
* is no longer accessible to the walker by this point.
*/
-void free_io_pgtable_ops(struct io_pgtable_ops *ops)
+void free_io_pgtable_ops(struct io_pgtable_ops *ops, struct page_info *page)
{
struct io_pgtable *iop;
@@ -86,5 +86,6 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops)
iop = container_of(ops, struct io_pgtable, ops);
io_pgtable_tlb_flush_all(iop);
- io_pgtable_init_table[iop->fmt]->free(iop);
+ iop->cookie = NULL;
+ io_pgtable_init_table[iop->fmt]->free(iop, page);
}
diff --git a/xen/drivers/passthrough/arm/io-pgtable.h b/xen/drivers/passthrough/arm/io-pgtable.h
index fb81fcf..df0e21b 100644
--- a/xen/drivers/passthrough/arm/io-pgtable.h
+++ b/xen/drivers/passthrough/arm/io-pgtable.h
@@ -144,7 +144,7 @@ struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
*
* @ops: The ops returned from alloc_io_pgtable_ops.
*/
-void free_io_pgtable_ops(struct io_pgtable_ops *ops);
+void free_io_pgtable_ops(struct io_pgtable_ops *ops, struct page_info *page);
/*
@@ -201,7 +201,7 @@ static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
*/
struct io_pgtable_init_fns {
struct io_pgtable *(*alloc)(struct io_pgtable_cfg *cfg, void *cookie);
- void (*free)(struct io_pgtable *iop);
+ void (*free)(struct io_pgtable *iop, struct page_info *page);
};
extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns;
diff --git a/xen/drivers/passthrough/arm/ipmmu-vmsa.c b/xen/drivers/passthrough/arm/ipmmu-vmsa.c
index e54b507..2a04800 100644
--- a/xen/drivers/passthrough/arm/ipmmu-vmsa.c
+++ b/xen/drivers/passthrough/arm/ipmmu-vmsa.c
@@ -708,8 +708,8 @@ static void ipmmu_tlb_flush_all(void *cookie)
{
struct ipmmu_vmsa_domain *domain = cookie;
- /* Xen: Just return if context_id has non-existent value */
- if (domain->context_id >= domain->root->num_ctx)
+ /* Xen: Just return if context is absent or context_id has non-existent value */
+ if (!domain || domain->context_id >= domain->root->num_ctx)
return;
ipmmu_tlb_invalidate(domain);
@@ -796,7 +796,9 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
*/
ret = ipmmu_domain_allocate_context(domain->root, domain);
if (ret < 0) {
- free_io_pgtable_ops(domain->iop);
+ /* Pass root page table for this domain as an argument. */
+ free_io_pgtable_ops(domain->iop,
+ maddr_to_page(domain->cfg.arm_lpae_s1_cfg.ttbr[0]));
return ret;
}
@@ -2193,7 +2195,12 @@ static void ipmmu_vmsa_destroy_domain(struct iommu_domain *io_domain)
* been detached.
*/
ipmmu_domain_destroy_context(domain);
- free_io_pgtable_ops(domain->iop);
+ /*
+ * Pass root page table for this domain as an argument.
+ * This call will lead to start deallocation sequence.
+ */
+ free_io_pgtable_ops(domain->iop,
+ maddr_to_page(domain->cfg.arm_lpae_s1_cfg.ttbr[0]));
}
kfree(domain);
@@ -2383,6 +2390,17 @@ static int ipmmu_vmsa_domain_init(struct domain *d, bool use_iommu)
return 0;
}
+/*
+ * Seems, there is one more page we need to process. So, retrieve
+ * the pointer and go on deallocation sequence.
+ */
+static void ipmmu_vmsa_free_page_table(struct page_info *page)
+{
+ struct io_pgtable_ops *ops = (struct io_pgtable_ops *)page->pad;
+
+ free_io_pgtable_ops(ops, page);
+}
+
static void __hwdom_init ipmmu_vmsa_hwdom_init(struct domain *d)
{
}
@@ -2404,6 +2422,12 @@ static void ipmmu_vmsa_domain_teardown(struct domain *d)
ASSERT(list_empty(&xen_domain->contexts));
xfree(xen_domain);
dom_iommu(d)->arch.priv = NULL;
+ /*
+ * After this point we have all domain resources deallocated, except
+ * page table which we will deallocate asynchronously. The IOMMU code
+ * provides us with iommu_pt_cleanup_list and free_page_table platform
+ * callback what we actually going to use.
+ */
}
static int __must_check ipmmu_vmsa_map_pages(struct domain *d,
@@ -2462,6 +2486,7 @@ static void ipmmu_vmsa_dump_p2m_table(struct domain *d)
static const struct iommu_ops ipmmu_vmsa_iommu_ops = {
.init = ipmmu_vmsa_domain_init,
.hwdom_init = ipmmu_vmsa_hwdom_init,
+ .free_page_table = ipmmu_vmsa_free_page_table,
.teardown = ipmmu_vmsa_domain_teardown,
.iotlb_flush = ipmmu_vmsa_iotlb_flush,
.assign_device = ipmmu_vmsa_assign_dev,
--
2.7.4
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
next prev parent reply other threads:[~2017-07-26 15:10 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-07-26 15:09 [RFC PATCH v1 0/7] IPMMU-VMSA support on ARM Oleksandr Tyshchenko
2017-07-26 15:09 ` [RFC PATCH v1 1/7] iommu/arm: ipmmu-vmsa: Add IPMMU-VMSA support Oleksandr Tyshchenko
2017-07-26 15:09 ` [RFC PATCH v1 2/7] iommu/arm: ipmmu-vmsa: Add Xen changes for main driver Oleksandr Tyshchenko
2017-08-08 11:34 ` Julien Grall
2017-08-10 14:27 ` Oleksandr Tyshchenko
2017-08-10 15:13 ` Julien Grall
2017-08-21 15:53 ` Oleksandr Tyshchenko
2017-08-23 11:41 ` Julien Grall
2017-08-25 20:06 ` Stefano Stabellini
2017-08-28 17:29 ` Oleksandr Tyshchenko
2017-07-26 15:10 ` [RFC PATCH v1 3/7] iommu/arm: ipmmu-vmsa: Add io-pgtables support Oleksandr Tyshchenko
2017-07-26 15:10 ` [RFC PATCH v1 4/7] iommu/arm: ipmmu-vmsa: Add Xen changes for io-pgtables Oleksandr Tyshchenko
2017-07-26 15:10 ` [RFC PATCH v1 5/7] iommu/arm: Build IPMMU-VMSA related stuff Oleksandr Tyshchenko
2017-07-26 15:10 ` Oleksandr Tyshchenko [this message]
2017-08-08 11:36 ` [RFC PATCH v1 6/7] iommu/arm: ipmmu-vmsa: Deallocate page table asynchronously Julien Grall
2017-07-26 15:10 ` [RFC PATCH v1 7/7] iommu/arm: ipmmu-vmsa: Enable VMSAv8-64 mode if IPMMU HW supports it Oleksandr Tyshchenko
2017-08-01 12:27 ` [RFC PATCH v1 0/7] IPMMU-VMSA support on ARM Julien Grall
2017-08-01 17:13 ` Oleksandr Tyshchenko
2017-08-08 11:21 ` Julien Grall
2017-08-08 16:52 ` Stefano Stabellini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1501081804-4882-7-git-send-email-olekstysh@gmail.com \
--to=olekstysh@gmail.com \
--cc=julien.grall@arm.com \
--cc=oleksandr_tyshchenko@epam.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).