2013-08-01 11:50:09

by Roger Pau Monne

[permalink] [raw]
Subject: [PATCH v1] p2m: use GNTTABOP_unmap_and_duplicate if available

The new GNTTABOP_unmap_and_duplicate operation doesn't zero the
mapping passed in new_addr, allowing us to perform batch unmaps in p2m
code without requiring the use of a multicall.

Signed-off-by: Roger Pau Monné <[email protected]>
Cc: Stefano Stabellini <[email protected]>
Cc: Konrad Rzeszutek Wilk <[email protected]>
Cc: David Vrabel <[email protected]>
---
Changes since RFC:
* Move shared code between _single and _batch to helper
functions.
---
I don't currently have a NFS server (the one I had is currently not
working due to SD card corruption) and I cannot set up one right now,
so I've only tested this with a raw image stored in a local disk.
---
arch/x86/include/asm/xen/page.h | 4 +-
arch/x86/xen/p2m.c | 154 +++++++++++++++++++++++++++++++----
drivers/xen/grant-table.c | 24 ++----
include/xen/interface/grant_table.h | 22 +++++
4 files changed, 169 insertions(+), 35 deletions(-)

diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
index 6aef9fb..ea1dce6 100644
--- a/arch/x86/include/asm/xen/page.h
+++ b/arch/x86/include/asm/xen/page.h
@@ -51,8 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,

extern int m2p_add_override(unsigned long mfn, struct page *page,
struct gnttab_map_grant_ref *kmap_op);
-extern int m2p_remove_override(struct page *page,
- struct gnttab_map_grant_ref *kmap_op);
+extern int m2p_remove_override(struct page **pages,
+ struct gnttab_map_grant_ref *kmap_ops, int count);
extern struct page *m2p_find_override(unsigned long mfn);
extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);

diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 0d4ec35..e92636c 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -154,6 +154,8 @@
#include <linux/hash.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/hardirq.h>

#include <asm/cache.h>
#include <asm/setup.h>
@@ -853,6 +855,7 @@ bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)

static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH);
static DEFINE_SPINLOCK(m2p_override_lock);
+extern bool gnttab_supports_duplicate;

static void __init m2p_override_init(void)
{
@@ -933,8 +936,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
return 0;
}
EXPORT_SYMBOL_GPL(m2p_add_override);
-int m2p_remove_override(struct page *page,
- struct gnttab_map_grant_ref *kmap_op)
+
+static inline int remove_page_override(struct page *page)
{
unsigned long flags;
unsigned long mfn;
@@ -965,6 +968,49 @@ int m2p_remove_override(struct page *page,
ClearPagePrivate(page);

set_phys_to_machine(pfn, page->index);
+
+ return 0;
+}
+
+static inline int check_duplicate_mfn(struct page *page, unsigned long mfn)
+{
+ unsigned long pfn;
+ int ret = 0;
+
+ /*
+ * p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
+ * somewhere in this domain, even before being added to the
+ * m2p_override (see comment above in m2p_add_override).
+ * If there are no other entries in the m2p_override corresponding
+ * to this mfn, then remove the FOREIGN_FRAME_BIT from the p2m for
+ * the original pfn (the one shared by the frontend): the backend
+ * cannot do any IO on this page anymore because it has been
+ * unshared. Removing the FOREIGN_FRAME_BIT from the p2m entry of
+ * the original pfn causes mfn_to_pfn(mfn) to return the frontend
+ * pfn again.
+ */
+ mfn &= ~FOREIGN_FRAME_BIT;
+ ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
+ if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
+ m2p_find_override(mfn) == NULL)
+ set_phys_to_machine(pfn, mfn);
+
+ return ret;
+}
+
+int m2p_remove_override_single(struct page *page,
+ struct gnttab_map_grant_ref *kmap_op)
+{
+ unsigned long mfn;
+ unsigned long pfn;
+ int ret = 0;
+
+ pfn = page_to_pfn(page);
+ mfn = get_phys_to_machine(pfn);
+ ret = remove_page_override(page);
+ if (ret)
+ return ret;
+
if (kmap_op != NULL) {
if (!PageHighMem(page)) {
struct multicall_space mcs;
@@ -1016,24 +1062,98 @@ int m2p_remove_override(struct page *page,
}
}

- /* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
- * somewhere in this domain, even before being added to the
- * m2p_override (see comment above in m2p_add_override).
- * If there are no other entries in the m2p_override corresponding
- * to this mfn, then remove the FOREIGN_FRAME_BIT from the p2m for
- * the original pfn (the one shared by the frontend): the backend
- * cannot do any IO on this page anymore because it has been
- * unshared. Removing the FOREIGN_FRAME_BIT from the p2m entry of
- * the original pfn causes mfn_to_pfn(mfn) to return the frontend
- * pfn again. */
- mfn &= ~FOREIGN_FRAME_BIT;
- ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
- if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
- m2p_find_override(mfn) == NULL)
- set_phys_to_machine(pfn, mfn);
+ ret = check_duplicate_mfn(page, mfn);
+ if (ret)
+ return ret;

return 0;
}
+
+int m2p_remove_override_batch(struct page **pages,
+ struct gnttab_map_grant_ref *kmap_ops, int count)
+{
+ struct gnttab_unmap_and_duplicate *unmap_ops = NULL;
+ unsigned long *mfn = kcalloc(count, sizeof(mfn[0]), GFP_KERNEL);
+ int ret = 0, i;
+
+ if (!mfn)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ mfn[i] = get_phys_to_machine(page_to_pfn(pages[i]));
+ ret = remove_page_override(pages[i]);
+ if (ret)
+ goto out;
+ }
+
+ if (kmap_ops != NULL) {
+ struct page *scratch_page = get_balloon_scratch_page();
+ unsigned long scratch_page_address = (unsigned long)
+ __va(page_to_pfn(scratch_page) << PAGE_SHIFT);
+ int invcount = 0;
+
+ unmap_ops = kcalloc(count, sizeof(unmap_ops[0]), GFP_KERNEL);
+ if (!unmap_ops) {
+ put_balloon_scratch_page();
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < count; i++) {
+ if (!PageHighMem(pages[i])) {
+ unmap_ops[invcount].host_addr = kmap_ops[i].host_addr;
+ unmap_ops[invcount].new_addr = scratch_page_address;
+ unmap_ops[invcount].handle = kmap_ops[i].handle;
+ invcount++;
+ }
+ }
+ ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_and_duplicate, unmap_ops, invcount);
+ if (ret) {
+ pr_warning("m2p_remove_override_batch: GNTTABOP_unmap_and_duplicate failed");
+ put_balloon_scratch_page();
+ ret = -1;
+ goto out;
+ }
+ put_balloon_scratch_page();
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = check_duplicate_mfn(pages[i], mfn[i]);
+ if (ret)
+ goto out;
+ }
+
+out:
+ kfree(unmap_ops);
+ kfree(mfn);
+ return ret;
+}
+
+int m2p_remove_override(struct page **pages,
+ struct gnttab_map_grant_ref *kmap_ops, int count)
+{
+ int i, ret;
+ bool lazy = false;
+
+ if (gnttab_supports_duplicate) {
+ ret = m2p_remove_override_batch(pages, kmap_ops, count);
+ if (ret)
+ return ret;
+ } else {
+ if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
+ arch_enter_lazy_mmu_mode();
+ lazy = true;
+ }
+ for (i = 0; i < count; i++) {
+ ret = m2p_remove_override_single(pages[i], kmap_ops ?
+ &kmap_ops[i] : NULL);
+ if (ret)
+ return ret;
+ }
+ if (lazy)
+ arch_leave_lazy_mmu_mode();
+ }
+ return ret;
+}
EXPORT_SYMBOL_GPL(m2p_remove_override);

struct page *m2p_find_override(unsigned long mfn)
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index d5418c1..835f600 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -64,6 +64,7 @@ static int gnttab_free_count;
static grant_ref_t gnttab_free_head;
static DEFINE_SPINLOCK(gnttab_list_lock);
unsigned long xen_hvm_resume_frames;
+bool gnttab_supports_duplicate;
EXPORT_SYMBOL_GPL(xen_hvm_resume_frames);

static union {
@@ -934,8 +935,7 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
struct gnttab_map_grant_ref *kmap_ops,
struct page **pages, unsigned int count)
{
- int i, ret;
- bool lazy = false;
+ int ret;

ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
if (ret)
@@ -944,20 +944,7 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
if (xen_feature(XENFEAT_auto_translated_physmap))
return ret;

- if (!in_interrupt() && paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
- arch_enter_lazy_mmu_mode();
- lazy = true;
- }
-
- for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i], kmap_ops ?
- &kmap_ops[i] : NULL);
- if (ret)
- return ret;
- }
-
- if (lazy)
- arch_leave_lazy_mmu_mode();
+ ret = m2p_remove_override(pages, kmap_ops, count);

return ret;
}
@@ -1247,6 +1234,11 @@ int gnttab_init(void)
gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES;
gnttab_free_head = NR_RESERVED_ENTRIES;

+ if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_and_duplicate, NULL, 0))
+ gnttab_supports_duplicate = false;
+ else
+ gnttab_supports_duplicate = true;
+
printk("Grant table initialized\n");
return 0;

diff --git a/include/xen/interface/grant_table.h b/include/xen/interface/grant_table.h
index e40fae9..1f49dc1 100644
--- a/include/xen/interface/grant_table.h
+++ b/include/xen/interface/grant_table.h
@@ -428,6 +428,27 @@ struct gnttab_unmap_and_replace {
DEFINE_GUEST_HANDLE_STRUCT(gnttab_unmap_and_replace);

/*
+ * GNTTABOP_unmap_and_duplicate: Destroy one or more grant-reference mappings
+ * tracked by <handle> but atomically replace the page table entry with one
+ * pointing to the machine address under <new_addr>.
+ * NOTES:
+ * 1. The call may fail in an undefined manner if either mapping is not
+ * tracked by <handle>.
+ * 2. After executing a batch of unmaps, it is guaranteed that no stale
+ * mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_and_duplicate 12
+struct gnttab_unmap_and_duplicate {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint64_t new_addr;
+ grant_handle_t handle;
+ /* OUT parameters. */
+ int16_t status; /* => enum grant_status */
+};
+DEFINE_GUEST_HANDLE_STRUCT(gnttab_unmap_and_duplicate_t);
+
+/*
* GNTTABOP_set_version: Request a particular version of the grant
* table shared table structure. This operation can only be performed
* once in any given domain. It must be performed before any grants
@@ -522,6 +543,7 @@ DEFINE_GUEST_HANDLE_STRUCT(gnttab_get_version);
#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */
#define GNTST_address_too_big (-11) /* transfer page address too large. */
#define GNTST_eagain (-12) /* Operation not done; try again. */
+#define GNTST_enosys (-13) /* Operation not implemented. */

#define GNTTABOP_error_msgs { \
"okay", \
--
1.7.7.5 (Apple Git-26)


2013-08-04 14:56:55

by Stefano Stabellini

[permalink] [raw]
Subject: Re: [PATCH v1] p2m: use GNTTABOP_unmap_and_duplicate if available

On Thu, 1 Aug 2013, Roger Pau Monne wrote:
> The new GNTTABOP_unmap_and_duplicate operation doesn't zero the
> mapping passed in new_addr, allowing us to perform batch unmaps in p2m
> code without requiring the use of a multicall.
>
> Signed-off-by: Roger Pau Monné <[email protected]>
> Cc: Stefano Stabellini <[email protected]>
> Cc: Konrad Rzeszutek Wilk <[email protected]>
> Cc: David Vrabel <[email protected]>

It looks pretty good overall.


> Changes since RFC:
> * Move shared code between _single and _batch to helper
> functions.
> ---
> I don't currently have a NFS server (the one I had is currently not
> working due to SD card corruption) and I cannot set up one right now,
> so I've only tested this with a raw image stored in a local disk.
> ---
> arch/x86/include/asm/xen/page.h | 4 +-
> arch/x86/xen/p2m.c | 154 +++++++++++++++++++++++++++++++----
> drivers/xen/grant-table.c | 24 ++----
> include/xen/interface/grant_table.h | 22 +++++
> 4 files changed, 169 insertions(+), 35 deletions(-)
>
> diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
> index 6aef9fb..ea1dce6 100644
> --- a/arch/x86/include/asm/xen/page.h
> +++ b/arch/x86/include/asm/xen/page.h
> @@ -51,8 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,
>
> extern int m2p_add_override(unsigned long mfn, struct page *page,
> struct gnttab_map_grant_ref *kmap_op);
> -extern int m2p_remove_override(struct page *page,
> - struct gnttab_map_grant_ref *kmap_op);
> +extern int m2p_remove_override(struct page **pages,
> + struct gnttab_map_grant_ref *kmap_ops, int count);
> extern struct page *m2p_find_override(unsigned long mfn);
> extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
>
> diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
> index 0d4ec35..e92636c 100644
> --- a/arch/x86/xen/p2m.c
> +++ b/arch/x86/xen/p2m.c
> @@ -154,6 +154,8 @@
> #include <linux/hash.h>
> #include <linux/sched.h>
> #include <linux/seq_file.h>
> +#include <linux/slab.h>
> +#include <linux/hardirq.h>
>
> #include <asm/cache.h>
> #include <asm/setup.h>
> @@ -853,6 +855,7 @@ bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
>
> static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH);
> static DEFINE_SPINLOCK(m2p_override_lock);
> +extern bool gnttab_supports_duplicate;

If you only use gnttab_supports_duplicate in the m2p, you might as well
make the variable static and initialize it from m2p_override_init.


> static void __init m2p_override_init(void)
> {
> @@ -933,8 +936,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
> return 0;
> }
> EXPORT_SYMBOL_GPL(m2p_add_override);
> -int m2p_remove_override(struct page *page,
> - struct gnttab_map_grant_ref *kmap_op)
> +
> +static inline int remove_page_override(struct page *page)
> {
> unsigned long flags;
> unsigned long mfn;
> @@ -965,6 +968,49 @@ int m2p_remove_override(struct page *page,
> ClearPagePrivate(page);
>
> set_phys_to_machine(pfn, page->index);
> +
> + return 0;
> +}
> +
> +static inline int check_duplicate_mfn(struct page *page, unsigned long mfn)
> +{
> + unsigned long pfn;
> + int ret = 0;
> +
> + /*
> + * p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
> + * somewhere in this domain, even before being added to the
> + * m2p_override (see comment above in m2p_add_override).
> + * If there are no other entries in the m2p_override corresponding
> + * to this mfn, then remove the FOREIGN_FRAME_BIT from the p2m for
> + * the original pfn (the one shared by the frontend): the backend
> + * cannot do any IO on this page anymore because it has been
> + * unshared. Removing the FOREIGN_FRAME_BIT from the p2m entry of
> + * the original pfn causes mfn_to_pfn(mfn) to return the frontend
> + * pfn again.
> + */
> + mfn &= ~FOREIGN_FRAME_BIT;
> + ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
> + if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
> + m2p_find_override(mfn) == NULL)
> + set_phys_to_machine(pfn, mfn);
> +
> + return ret;
> +}

There is no need to keep check_duplicate_mfn separate from
remove_page_override, you can just "append" this code at the end of
remove_page_override.

2013-08-27 10:10:39

by Roger Pau Monne

[permalink] [raw]
Subject: Re: [PATCH v1] p2m: use GNTTABOP_unmap_and_duplicate if available

On 04/08/13 16:56, Stefano Stabellini wrote:
> On Thu, 1 Aug 2013, Roger Pau Monne wrote:
>> The new GNTTABOP_unmap_and_duplicate operation doesn't zero the
>> mapping passed in new_addr, allowing us to perform batch unmaps in p2m
>> code without requiring the use of a multicall.
>>
>> Signed-off-by: Roger Pau Monné <[email protected]>
>> Cc: Stefano Stabellini <[email protected]>
>> Cc: Konrad Rzeszutek Wilk <[email protected]>
>> Cc: David Vrabel <[email protected]>
>
> It looks pretty good overall.
>
>
>> Changes since RFC:
>> * Move shared code between _single and _batch to helper
>> functions.
>> ---
>> I don't currently have a NFS server (the one I had is currently not
>> working due to SD card corruption) and I cannot set up one right now,
>> so I've only tested this with a raw image stored in a local disk.
>> ---
>> arch/x86/include/asm/xen/page.h | 4 +-
>> arch/x86/xen/p2m.c | 154 +++++++++++++++++++++++++++++++----
>> drivers/xen/grant-table.c | 24 ++----
>> include/xen/interface/grant_table.h | 22 +++++
>> 4 files changed, 169 insertions(+), 35 deletions(-)
>>
>> diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
>> index 6aef9fb..ea1dce6 100644
>> --- a/arch/x86/include/asm/xen/page.h
>> +++ b/arch/x86/include/asm/xen/page.h
>> @@ -51,8 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,
>>
>> extern int m2p_add_override(unsigned long mfn, struct page *page,
>> struct gnttab_map_grant_ref *kmap_op);
>> -extern int m2p_remove_override(struct page *page,
>> - struct gnttab_map_grant_ref *kmap_op);
>> +extern int m2p_remove_override(struct page **pages,
>> + struct gnttab_map_grant_ref *kmap_ops, int count);
>> extern struct page *m2p_find_override(unsigned long mfn);
>> extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
>>
>> diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
>> index 0d4ec35..e92636c 100644
>> --- a/arch/x86/xen/p2m.c
>> +++ b/arch/x86/xen/p2m.c
>> @@ -154,6 +154,8 @@
>> #include <linux/hash.h>
>> #include <linux/sched.h>
>> #include <linux/seq_file.h>
>> +#include <linux/slab.h>
>> +#include <linux/hardirq.h>
>>
>> #include <asm/cache.h>
>> #include <asm/setup.h>
>> @@ -853,6 +855,7 @@ bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
>>
>> static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH);
>> static DEFINE_SPINLOCK(m2p_override_lock);
>> +extern bool gnttab_supports_duplicate;
>
> If you only use gnttab_supports_duplicate in the m2p, you might as well
> make the variable static and initialize it from m2p_override_init.

m2p_override_init is called way too early in the boot process, if I try
to perform the hypercall there I get a general protection fault.

>> static void __init m2p_override_init(void)
>> {
>> @@ -933,8 +936,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
>> return 0;
>> }
>> EXPORT_SYMBOL_GPL(m2p_add_override);
>> -int m2p_remove_override(struct page *page,
>> - struct gnttab_map_grant_ref *kmap_op)
>> +
>> +static inline int remove_page_override(struct page *page)
>> {
>> unsigned long flags;
>> unsigned long mfn;
>> @@ -965,6 +968,49 @@ int m2p_remove_override(struct page *page,
>> ClearPagePrivate(page);
>>
>> set_phys_to_machine(pfn, page->index);
>> +
>> + return 0;
>> +}
>> +
>> +static inline int check_duplicate_mfn(struct page *page, unsigned long mfn)
>> +{
>> + unsigned long pfn;
>> + int ret = 0;
>> +
>> + /*
>> + * p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
>> + * somewhere in this domain, even before being added to the
>> + * m2p_override (see comment above in m2p_add_override).
>> + * If there are no other entries in the m2p_override corresponding
>> + * to this mfn, then remove the FOREIGN_FRAME_BIT from the p2m for
>> + * the original pfn (the one shared by the frontend): the backend
>> + * cannot do any IO on this page anymore because it has been
>> + * unshared. Removing the FOREIGN_FRAME_BIT from the p2m entry of
>> + * the original pfn causes mfn_to_pfn(mfn) to return the frontend
>> + * pfn again.
>> + */
>> + mfn &= ~FOREIGN_FRAME_BIT;
>> + ret = __get_user(pfn, &machine_to_phys_mapping[mfn]);
>> + if (ret == 0 && get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
>> + m2p_find_override(mfn) == NULL)
>> + set_phys_to_machine(pfn, mfn);
>> +
>> + return ret;
>> +}
>
> There is no need to keep check_duplicate_mfn separate from
> remove_page_override, you can just "append" this code at the end of
> remove_page_override.

Done, thanks for the review.