]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Xen/gnttab: handle p2m update errors on a per-slot basis
authorJan Beulich <jbeulich@suse.com>
Thu, 25 Feb 2021 15:34:43 +0000 (16:34 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 7 Mar 2021 10:24:22 +0000 (11:24 +0100)
commit 8310b77b48c5558c140e7a57a702e7819e62f04e upstream.

Bailing immediately from set_foreign_p2m_mapping() upon a p2m updating
error leaves the full batch in an ambiguous state as far as the caller
is concerned. Instead flags respective slots as bad, unmapping what
was mapped there right away.

HYPERVISOR_grant_table_op()'s return value and the individual unmap
slots' status fields get used only for a one-time - there's not much we
can do in case of a failure.

Note that there's no GNTST_enomem or alike, so GNTST_general_error gets
used.

The map ops' handle fields get overwritten just to be on the safe side.

This is part of XSA-367.

Cc: <stable@vger.kernel.org>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Link: https://lore.kernel.org/r/96cccf5d-e756-5f53-b91a-ea269bfb9be0@suse.com
Signed-off-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm/xen/p2m.c
arch/x86/xen/p2m.c

index 02579e6569f0c8de0366f40a258b64a9142c1191..b4ec8d1b0befd5886d4164a9d15f825c77e04b93 100644 (file)
@@ -91,12 +91,39 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
        int i;
 
        for (i = 0; i < count; i++) {
+               struct gnttab_unmap_grant_ref unmap;
+               int rc;
+
                if (map_ops[i].status)
                        continue;
-               if (unlikely(!set_phys_to_machine(map_ops[i].host_addr >> XEN_PAGE_SHIFT,
-                                   map_ops[i].dev_bus_addr >> XEN_PAGE_SHIFT))) {
-                       return -ENOMEM;
-               }
+               if (likely(set_phys_to_machine(map_ops[i].host_addr >> XEN_PAGE_SHIFT,
+                                   map_ops[i].dev_bus_addr >> XEN_PAGE_SHIFT)))
+                       continue;
+
+               /*
+                * Signal an error for this slot. This in turn requires
+                * immediate unmapping.
+                */
+               map_ops[i].status = GNTST_general_error;
+               unmap.host_addr = map_ops[i].host_addr,
+               unmap.handle = map_ops[i].handle;
+               map_ops[i].handle = ~0;
+               if (map_ops[i].flags & GNTMAP_device_map)
+                       unmap.dev_bus_addr = map_ops[i].dev_bus_addr;
+               else
+                       unmap.dev_bus_addr = 0;
+
+               /*
+                * Pre-populate the status field, to be recognizable in
+                * the log message below.
+                */
+               unmap.status = 1;
+
+               rc = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+                                              &unmap, 1);
+               if (rc || unmap.status != GNTST_okay)
+                       pr_err_once("gnttab unmap failed: rc=%d st=%d\n",
+                                   rc, unmap.status);
        }
 
        return 0;
index 8c7c5bb94257e3fe36886569fa5d321438acf372..86047b18b013607988bfde7f536b83fcfc7034e5 100644 (file)
@@ -723,6 +723,8 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
 
        for (i = 0; i < count; i++) {
                unsigned long mfn, pfn;
+               struct gnttab_unmap_grant_ref unmap[2];
+               int rc;
 
                /* Do not add to override if the map failed. */
                if (map_ops[i].status != GNTST_okay ||
@@ -740,10 +742,46 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
 
                WARN(pfn_to_mfn(pfn) != INVALID_P2M_ENTRY, "page must be ballooned");
 
-               if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)))) {
-                       ret = -ENOMEM;
-                       goto out;
+               if (likely(set_phys_to_machine(pfn, FOREIGN_FRAME(mfn))))
+                       continue;
+
+               /*
+                * Signal an error for this slot. This in turn requires
+                * immediate unmapping.
+                */
+               map_ops[i].status = GNTST_general_error;
+               unmap[0].host_addr = map_ops[i].host_addr,
+               unmap[0].handle = map_ops[i].handle;
+               map_ops[i].handle = ~0;
+               if (map_ops[i].flags & GNTMAP_device_map)
+                       unmap[0].dev_bus_addr = map_ops[i].dev_bus_addr;
+               else
+                       unmap[0].dev_bus_addr = 0;
+
+               if (kmap_ops) {
+                       kmap_ops[i].status = GNTST_general_error;
+                       unmap[1].host_addr = kmap_ops[i].host_addr,
+                       unmap[1].handle = kmap_ops[i].handle;
+                       kmap_ops[i].handle = ~0;
+                       if (kmap_ops[i].flags & GNTMAP_device_map)
+                               unmap[1].dev_bus_addr = kmap_ops[i].dev_bus_addr;
+                       else
+                               unmap[1].dev_bus_addr = 0;
                }
+
+               /*
+                * Pre-populate both status fields, to be recognizable in
+                * the log message below.
+                */
+               unmap[0].status = 1;
+               unmap[1].status = 1;
+
+               rc = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+                                              unmap, 1 + !!kmap_ops);
+               if (rc || unmap[0].status != GNTST_okay ||
+                   unmap[1].status != GNTST_okay)
+                       pr_err_once("gnttab unmap failed: rc=%d st0=%d st1=%d\n",
+                                   rc, unmap[0].status, unmap[1].status);
        }
 
 out: