virt: vbox: Use __get_free_pages instead of kmalloc for DMA32 memory
authorHans de Goede <hdegoede@redhat.com>
Wed, 18 Apr 2018 13:24:49 +0000 (15:24 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Apr 2018 11:41:55 +0000 (13:41 +0200)
It is not possible to get DMA32 zone memory through kmalloc, causing
the vboxguest driver to malfunction due to getting memory above
4G which the PCI device cannot handle.

This commit changes the kmalloc calls where the 4G limit matters to
using __get_free_pages() fixing vboxguest not working on x86_64 guests
with more then 4G RAM.

Cc: stable@vger.kernel.org
Reported-by: Eloy Coto Pereiro <eloy.coto@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/virt/vboxguest/vboxguest_linux.c
drivers/virt/vboxguest/vboxguest_utils.c

index 82e280d38cc2e1bd1da2b2e6847b3174b5854548..398d2269323471305088762ba92ed9da3d53d9af 100644 (file)
@@ -87,6 +87,7 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
        struct vbg_session *session = filp->private_data;
        size_t returned_size, size;
        struct vbg_ioctl_hdr hdr;
+       bool is_vmmdev_req;
        int ret = 0;
        void *buf;
 
@@ -106,8 +107,17 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
        if (size > SZ_16M)
                return -E2BIG;
 
-       /* __GFP_DMA32 because IOCTL_VMMDEV_REQUEST passes this to the host */
-       buf = kmalloc(size, GFP_KERNEL | __GFP_DMA32);
+       /*
+        * IOCTL_VMMDEV_REQUEST needs the buffer to be below 4G to avoid
+        * the need for a bounce-buffer and another copy later on.
+        */
+       is_vmmdev_req = (req & ~IOCSIZE_MASK) == VBG_IOCTL_VMMDEV_REQUEST(0) ||
+                        req == VBG_IOCTL_VMMDEV_REQUEST_BIG;
+
+       if (is_vmmdev_req)
+               buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT);
+       else
+               buf = kmalloc(size, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
@@ -132,7 +142,10 @@ static long vbg_misc_device_ioctl(struct file *filp, unsigned int req,
                ret = -EFAULT;
 
 out:
-       kfree(buf);
+       if (is_vmmdev_req)
+               vbg_req_free(buf, size);
+       else
+               kfree(buf);
 
        return ret;
 }
index bad9154633593140e934cbb02be9b5979e050705..bf4474214b4d31bb708c3d9c302d6ce415e17c7a 100644 (file)
@@ -65,8 +65,9 @@ VBG_LOG(vbg_debug, pr_debug);
 void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type)
 {
        struct vmmdev_request_header *req;
+       int order = get_order(PAGE_ALIGN(len));
 
-       req = kmalloc(len, GFP_KERNEL | __GFP_DMA32);
+       req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order);
        if (!req)
                return NULL;
 
@@ -87,7 +88,7 @@ void vbg_req_free(void *req, size_t len)
        if (!req)
                return;
 
-       kfree(req);
+       free_pages((unsigned long)req, get_order(PAGE_ALIGN(len)));
 }
 
 /* Note this function returns a VBox status code, not a negative errno!! */