USB devices cannot perform DMA and hence have no dma_mask set in their
device structure. Therefore importing dmabuf into a USB-based driver
fails, which breaks joining and mirroring of display in X11.
For USB devices, pick the associated USB controller as attachment device.
This allows the DRM import helpers to perform the DMA setup. If the DMA
controller does not support DMA transfers, we're out of luck and cannot
import. Our current USB-based DRM drivers don't use DMA, so the actual
DMA device is not important.
Tested by joining/mirroring displays of udl and radeon under Gnome/X11.
v8:
* release dmadev if device initialization fails (Noralf)
* fix commit description (Noralf)
v7:
* fix use-before-init bug in gm12u320 (Dan)
v6:
* implement workaround in DRM drivers and hold reference to
DMA device while USB device is in use
* remove dev_is_usb() (Greg)
* collapse USB helper into usb_intf_get_dma_device() (Alan)
* integrate Daniel's TODO statement (Daniel)
* fix typos (Greg)
v5:
* provide a helper for USB interfaces (Alan)
* add FIXME item to documentation and TODO list (Daniel)
v4:
* implement workaround with USB helper functions (Greg)
* use struct usb_device->bus->sysdev as DMA device (Takashi)
v3:
* drop gem_create_object
* use DMA mask of USB controller, if any (Daniel, Christian, Noralf)
v2:
* move fix to importer side (Christian, Daniel)
* update SHMEM and CMA helpers for new PRIME callbacks
Signed-off-by: Thomas Zimmermann <tzimmermann(a)suse.de>
Fixes: 6eb0233ec2d0 ("usb: don't inherity DMA properties for USB devices")
Tested-by: Pavel Machek <pavel(a)ucw.cz>
Reviewed-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Acked-by: Christian König <christian.koenig(a)amd.com>
Acked-by: Daniel Vetter <daniel.vetter(a)ffwll.ch>
Cc: Christoph Hellwig <hch(a)lst.de>
Cc: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Cc: <stable(a)vger.kernel.org> # v5.10+
Signed-off-by: Thomas Zimmermann <tzimmermann(a)suse.de>
---
Documentation/gpu/todo.rst | 21 ++++++++++++++++
drivers/gpu/drm/tiny/gm12u320.c | 44 +++++++++++++++++++++++++++------
drivers/gpu/drm/udl/udl_drv.c | 17 +++++++++++++
drivers/gpu/drm/udl/udl_drv.h | 1 +
drivers/gpu/drm/udl/udl_main.c | 10 ++++++++
drivers/usb/core/usb.c | 32 ++++++++++++++++++++++++
include/linux/usb.h | 2 ++
7 files changed, 119 insertions(+), 8 deletions(-)
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 0631b9b323d5..fdfd6a1081ec 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -571,6 +571,27 @@ Contact: Daniel Vetter
Level: Intermediate
+Remove automatic page mapping from dma-buf importing
+----------------------------------------------------
+
+When importing dma-bufs, the dma-buf and PRIME frameworks automatically map
+imported pages into the importer's DMA area. drm_gem_prime_fd_to_handle() and
+drm_gem_prime_handle_to_fd() require that importers call dma_buf_attach()
+even if they never do actual device DMA, but only CPU access through
+dma_buf_vmap(). This is a problem for USB devices, which do not support DMA
+operations.
+
+To fix the issue, automatic page mappings should be removed from the
+buffer-sharing code. Fixing this is a bit more involved, since the import/export
+cache is also tied to &drm_gem_object.import_attach. Meanwhile we paper over
+this problem for USB devices by fishing out the USB host controller device, as
+long as that supports DMA. Otherwise importing can still needlessly fail.
+
+Contact: Thomas Zimmermann <tzimmermann(a)suse.de>, Daniel Vetter
+
+Level: Advanced
+
+
Better Testing
==============
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index 0b4f4f2af1ef..a233c86d428b 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -84,6 +84,7 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
struct gm12u320_device {
struct drm_device dev;
+ struct device *dmadev;
struct drm_simple_display_pipe pipe;
struct drm_connector conn;
unsigned char *cmd_buf;
@@ -599,6 +600,22 @@ static const uint64_t gm12u320_pipe_modifiers[] = {
DRM_FORMAT_MOD_INVALID
};
+/*
+ * FIXME: Dma-buf sharing requires DMA support by the importing device.
+ * This function is a workaround to make USB devices work as well.
+ * See todo.rst for how to fix the issue in the dma-buf framework.
+ */
+static struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct gm12u320_device *gm12u320 = to_gm12u320(dev);
+
+ if (!gm12u320->dmadev)
+ return ERR_PTR(-ENODEV);
+
+ return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
+}
+
DEFINE_DRM_GEM_FOPS(gm12u320_fops);
static const struct drm_driver gm12u320_drm_driver = {
@@ -612,6 +629,7 @@ static const struct drm_driver gm12u320_drm_driver = {
.fops = &gm12u320_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
+ .gem_prime_import = gm12u320_gem_prime_import,
};
static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
@@ -638,15 +656,18 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
struct gm12u320_device, dev);
if (IS_ERR(gm12u320))
return PTR_ERR(gm12u320);
+ dev = &gm12u320->dev;
+
+ gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
+ if (!gm12u320->dmadev)
+ drm_warn(dev, "buffer sharing not supported"); /* not an error */
INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
mutex_init(&gm12u320->fb_update.lock);
- dev = &gm12u320->dev;
-
ret = drmm_mode_config_init(dev);
if (ret)
- return ret;
+ goto err_put_device;
dev->mode_config.min_width = GM12U320_USER_WIDTH;
dev->mode_config.max_width = GM12U320_USER_WIDTH;
@@ -656,15 +677,15 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
ret = gm12u320_usb_alloc(gm12u320);
if (ret)
- return ret;
+ goto err_put_device;
ret = gm12u320_set_ecomode(gm12u320);
if (ret)
- return ret;
+ goto err_put_device;
ret = gm12u320_conn_init(gm12u320);
if (ret)
- return ret;
+ goto err_put_device;
ret = drm_simple_display_pipe_init(&gm12u320->dev,
&gm12u320->pipe,
@@ -674,24 +695,31 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
gm12u320_pipe_modifiers,
&gm12u320->conn);
if (ret)
- return ret;
+ goto err_put_device;
drm_mode_config_reset(dev);
usb_set_intfdata(interface, dev);
ret = drm_dev_register(dev, 0);
if (ret)
- return ret;
+ goto err_put_device;
drm_fbdev_generic_setup(dev, 0);
return 0;
+
+err_put_device:
+ put_device(gm12u320->dmadev);
+ return ret;
}
static void gm12u320_usb_disconnect(struct usb_interface *interface)
{
struct drm_device *dev = usb_get_intfdata(interface);
+ struct gm12u320_device *gm12u320 = to_gm12u320(dev);
+ put_device(gm12u320->dmadev);
+ gm12u320->dmadev = NULL;
drm_dev_unplug(dev);
drm_atomic_helper_shutdown(dev);
}
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 9269092697d8..5703277c6f52 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -32,6 +32,22 @@ static int udl_usb_resume(struct usb_interface *interface)
return drm_mode_config_helper_resume(dev);
}
+/*
+ * FIXME: Dma-buf sharing requires DMA support by the importing device.
+ * This function is a workaround to make USB devices work as well.
+ * See todo.rst for how to fix the issue in the dma-buf framework.
+ */
+static struct drm_gem_object *udl_driver_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct udl_device *udl = to_udl(dev);
+
+ if (!udl->dmadev)
+ return ERR_PTR(-ENODEV);
+
+ return drm_gem_prime_import_dev(dev, dma_buf, udl->dmadev);
+}
+
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
static const struct drm_driver driver = {
@@ -40,6 +56,7 @@ static const struct drm_driver driver = {
/* GEM hooks */
.fops = &udl_driver_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
+ .gem_prime_import = udl_driver_gem_prime_import,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index 875e73551ae9..cc16a13316e4 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -50,6 +50,7 @@ struct urb_list {
struct udl_device {
struct drm_device drm;
struct device *dev;
+ struct device *dmadev;
struct drm_simple_display_pipe display_pipe;
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index 0e2a376cb075..853f147036f6 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -315,6 +315,10 @@ int udl_init(struct udl_device *udl)
DRM_DEBUG("\n");
+ udl->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
+ if (!udl->dmadev)
+ drm_warn(dev, "buffer sharing not supported"); /* not an error */
+
mutex_init(&udl->gem_lock);
if (!udl_parse_vendor_descriptor(udl)) {
@@ -343,12 +347,18 @@ int udl_init(struct udl_device *udl)
err:
if (udl->urbs.count)
udl_free_urb_list(dev);
+ put_device(udl->dmadev);
DRM_ERROR("%d\n", ret);
return ret;
}
int udl_drop_usb(struct drm_device *dev)
{
+ struct udl_device *udl = to_udl(dev);
+
udl_free_urb_list(dev);
+ put_device(udl->dmadev);
+ udl->dmadev = NULL;
+
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8f07b0516100..a566bb494e24 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -748,6 +748,38 @@ void usb_put_intf(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_put_intf);
+/**
+ * usb_intf_get_dma_device - acquire a reference on the usb interface's DMA endpoint
+ * @intf: the usb interface
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_intf_get_dma_device() returns the DMA endpoint
+ * for the given USB interface, if any. The returned device structure must be
+ * released with put_device().
+ *
+ * See also usb_get_dma_device().
+ *
+ * Returns: A reference to the usb interface's DMA endpoint; or NULL if none
+ * exists.
+ */
+struct device *usb_intf_get_dma_device(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dmadev;
+
+ if (!udev->bus)
+ return NULL;
+
+ dmadev = get_device(udev->bus->sysdev);
+ if (!dmadev || !dmadev->dma_mask) {
+ put_device(dmadev);
+ return NULL;
+ }
+
+ return dmadev;
+}
+EXPORT_SYMBOL_GPL(usb_intf_get_dma_device);
+
/* USB device locking
*
* USB devices and interfaces are locked using the semaphore in their
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 7d72c4e0713c..d6a41841b93e 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -746,6 +746,8 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
extern int usb_reset_device(struct usb_device *dev);
extern void usb_queue_reset_device(struct usb_interface *dev);
+extern struct device *usb_intf_get_dma_device(struct usb_interface *intf);
+
#ifdef CONFIG_ACPI
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
bool enable);
base-commit: b80182b9bffb51460e4a2ad7d96737ecd49126dc
prerequisite-patch-id: c2b2f08f0eccc9f5df0c0da49fa1d36267deb11d
--
2.30.1
USB devices cannot perform DMA and hence have no dma_mask set in their
device structure. Therefore importing dmabuf into a USB-based driver
fails, which breaks joining and mirroring of display in X11.
For USB devices, pick the associated USB controller as attachment device.
This allows the DRM import helpers to perform the DMA setup. If the DMA
controller does not support DMA transfers, we're out of luck and cannot
import. Our current USB-based DRM drivers don't use DMA, so the actual
DMA device is not important.
Drivers should use DRM_GEM_SHMEM_DROVER_OPS_USB to initialize their
instance of struct drm_driver.
Tested by joining/mirroring displays of udl and radeon un der Gnome/X11.
v7:
* fix use-before-init bug in gm12u320 (Dan)
v6:
* implement workaround in DRM drivers and hold reference to
DMA device while USB device is in use
* remove dev_is_usb() (Greg)
* collapse USB helper into usb_intf_get_dma_device() (Alan)
* integrate Daniel's TODO statement (Daniel)
* fix typos (Greg)
v5:
* provide a helper for USB interfaces (Alan)
* add FIXME item to documentation and TODO list (Daniel)
v4:
* implement workaround with USB helper functions (Greg)
* use struct usb_device->bus->sysdev as DMA device (Takashi)
v3:
* drop gem_create_object
* use DMA mask of USB controller, if any (Daniel, Christian, Noralf)
v2:
* move fix to importer side (Christian, Daniel)
* update SHMEM and CMA helpers for new PRIME callbacks
Signed-off-by: Thomas Zimmermann <tzimmermann(a)suse.de>
Fixes: 6eb0233ec2d0 ("usb: don't inherity DMA properties for USB devices")
Tested-by: Pavel Machek <pavel(a)ucw.cz>
Reviewed-by: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Acked-by: Christian König <christian.koenig(a)amd.com>
Acked-by: Daniel Vetter <daniel.vetter(a)ffwll.ch>
Cc: Christoph Hellwig <hch(a)lst.de>
Cc: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Cc: <stable(a)vger.kernel.org> # v5.10+
Signed-off-by: Thomas Zimmermann <tzimmermann(a)suse.de>
---
Documentation/gpu/todo.rst | 21 +++++++++++++++++++++
drivers/gpu/drm/tiny/gm12u320.c | 28 ++++++++++++++++++++++++++--
drivers/gpu/drm/udl/udl_drv.c | 17 +++++++++++++++++
drivers/gpu/drm/udl/udl_drv.h | 1 +
drivers/gpu/drm/udl/udl_main.c | 9 +++++++++
drivers/usb/core/usb.c | 32 ++++++++++++++++++++++++++++++++
include/linux/usb.h | 2 ++
7 files changed, 108 insertions(+), 2 deletions(-)
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 0631b9b323d5..fdfd6a1081ec 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -571,6 +571,27 @@ Contact: Daniel Vetter
Level: Intermediate
+Remove automatic page mapping from dma-buf importing
+----------------------------------------------------
+
+When importing dma-bufs, the dma-buf and PRIME frameworks automatically map
+imported pages into the importer's DMA area. drm_gem_prime_fd_to_handle() and
+drm_gem_prime_handle_to_fd() require that importers call dma_buf_attach()
+even if they never do actual device DMA, but only CPU access through
+dma_buf_vmap(). This is a problem for USB devices, which do not support DMA
+operations.
+
+To fix the issue, automatic page mappings should be removed from the
+buffer-sharing code. Fixing this is a bit more involved, since the import/export
+cache is also tied to &drm_gem_object.import_attach. Meanwhile we paper over
+this problem for USB devices by fishing out the USB host controller device, as
+long as that supports DMA. Otherwise importing can still needlessly fail.
+
+Contact: Thomas Zimmermann <tzimmermann(a)suse.de>, Daniel Vetter
+
+Level: Advanced
+
+
Better Testing
==============
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index 0b4f4f2af1ef..4fe372f43cf5 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -84,6 +84,7 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
struct gm12u320_device {
struct drm_device dev;
+ struct device *dmadev;
struct drm_simple_display_pipe pipe;
struct drm_connector conn;
unsigned char *cmd_buf;
@@ -599,6 +600,22 @@ static const uint64_t gm12u320_pipe_modifiers[] = {
DRM_FORMAT_MOD_INVALID
};
+/*
+ * FIXME: Dma-buf sharing requires DMA support by the importing device.
+ * This function is a workaround to make USB devices work as well.
+ * See todo.rst for how to fix the issue in the dma-buf framework.
+ */
+static struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct gm12u320_device *gm12u320 = to_gm12u320(dev);
+
+ if (!gm12u320->dmadev)
+ return ERR_PTR(-ENODEV);
+
+ return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
+}
+
DEFINE_DRM_GEM_FOPS(gm12u320_fops);
static const struct drm_driver gm12u320_drm_driver = {
@@ -612,6 +629,7 @@ static const struct drm_driver gm12u320_drm_driver = {
.fops = &gm12u320_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
+ .gem_prime_import = gm12u320_gem_prime_import,
};
static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
@@ -638,12 +656,15 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
struct gm12u320_device, dev);
if (IS_ERR(gm12u320))
return PTR_ERR(gm12u320);
+ dev = &gm12u320->dev;
+
+ gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
+ if (!gm12u320->dmadev)
+ drm_warn(dev, "buffer sharing not supported"); /* not an error */
INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
mutex_init(&gm12u320->fb_update.lock);
- dev = &gm12u320->dev;
-
ret = drmm_mode_config_init(dev);
if (ret)
return ret;
@@ -691,7 +712,10 @@ static int gm12u320_usb_probe(struct usb_interface *interface,
static void gm12u320_usb_disconnect(struct usb_interface *interface)
{
struct drm_device *dev = usb_get_intfdata(interface);
+ struct gm12u320_device *gm12u320 = to_gm12u320(dev);
+ put_device(gm12u320->dmadev);
+ gm12u320->dmadev = NULL;
drm_dev_unplug(dev);
drm_atomic_helper_shutdown(dev);
}
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 9269092697d8..5703277c6f52 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -32,6 +32,22 @@ static int udl_usb_resume(struct usb_interface *interface)
return drm_mode_config_helper_resume(dev);
}
+/*
+ * FIXME: Dma-buf sharing requires DMA support by the importing device.
+ * This function is a workaround to make USB devices work as well.
+ * See todo.rst for how to fix the issue in the dma-buf framework.
+ */
+static struct drm_gem_object *udl_driver_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct udl_device *udl = to_udl(dev);
+
+ if (!udl->dmadev)
+ return ERR_PTR(-ENODEV);
+
+ return drm_gem_prime_import_dev(dev, dma_buf, udl->dmadev);
+}
+
DEFINE_DRM_GEM_FOPS(udl_driver_fops);
static const struct drm_driver driver = {
@@ -40,6 +56,7 @@ static const struct drm_driver driver = {
/* GEM hooks */
.fops = &udl_driver_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
+ .gem_prime_import = udl_driver_gem_prime_import,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
index 875e73551ae9..cc16a13316e4 100644
--- a/drivers/gpu/drm/udl/udl_drv.h
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -50,6 +50,7 @@ struct urb_list {
struct udl_device {
struct drm_device drm;
struct device *dev;
+ struct device *dmadev;
struct drm_simple_display_pipe display_pipe;
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
index 0e2a376cb075..7c0338bcadea 100644
--- a/drivers/gpu/drm/udl/udl_main.c
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -315,6 +315,10 @@ int udl_init(struct udl_device *udl)
DRM_DEBUG("\n");
+ udl->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
+ if (!udl->dmadev)
+ drm_warn(dev, "buffer sharing not supported"); /* not an error */
+
mutex_init(&udl->gem_lock);
if (!udl_parse_vendor_descriptor(udl)) {
@@ -349,6 +353,11 @@ int udl_init(struct udl_device *udl)
int udl_drop_usb(struct drm_device *dev)
{
+ struct udl_device *udl = to_udl(dev);
+
udl_free_urb_list(dev);
+ put_device(udl->dmadev);
+ udl->dmadev = NULL;
+
return 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8f07b0516100..a566bb494e24 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -748,6 +748,38 @@ void usb_put_intf(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_put_intf);
+/**
+ * usb_intf_get_dma_device - acquire a reference on the usb interface's DMA endpoint
+ * @intf: the usb interface
+ *
+ * While a USB device cannot perform DMA operations by itself, many USB
+ * controllers can. A call to usb_intf_get_dma_device() returns the DMA endpoint
+ * for the given USB interface, if any. The returned device structure must be
+ * released with put_device().
+ *
+ * See also usb_get_dma_device().
+ *
+ * Returns: A reference to the usb interface's DMA endpoint; or NULL if none
+ * exists.
+ */
+struct device *usb_intf_get_dma_device(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dmadev;
+
+ if (!udev->bus)
+ return NULL;
+
+ dmadev = get_device(udev->bus->sysdev);
+ if (!dmadev || !dmadev->dma_mask) {
+ put_device(dmadev);
+ return NULL;
+ }
+
+ return dmadev;
+}
+EXPORT_SYMBOL_GPL(usb_intf_get_dma_device);
+
/* USB device locking
*
* USB devices and interfaces are locked using the semaphore in their
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 7d72c4e0713c..d6a41841b93e 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -746,6 +746,8 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
extern int usb_reset_device(struct usb_device *dev);
extern void usb_queue_reset_device(struct usb_interface *dev);
+extern struct device *usb_intf_get_dma_device(struct usb_interface *intf);
+
#ifdef CONFIG_ACPI
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
bool enable);
base-commit: b80182b9bffb51460e4a2ad7d96737ecd49126dc
prerequisite-patch-id: c2b2f08f0eccc9f5df0c0da49fa1d36267deb11d
--
2.30.1
Hello,
Good day, please accept my apology for sending you this sensitive
information without per informing you. This is due to the urgency
of the information. I am a Project director at Ghana National
Petroleum Corporation (GNPC) and a member of Contract Tenders
Board of the Ministry of Mineral Resources in Ghana. I need your
service in a confidential matter regarding money transfer from an
over-invoiced contract sum. This requires a private arrangement
though the details of the transaction will be furnished to you if
you indicate your interest in this proposal.
We have all the legal documents to back up the transaction,
besides we have worked out the modalities to ensure smooth
conclusion of this fund transfer. We are willing to offer you
25% and there is not risk involved in the transaction.
I am expecting to hear from you, if you have interest in the
business. In which case, correspondence in the course of the
transaction will be conducted by email presently.
Thanks,
Yours Faithfully,
Dr. Joseph Dabantu
Commit 56348560d495 ("debugfs: do not attempt to create a new file
before the filesystem is initalized") forbids creating new debugfs files
until debugfs is fully initialized. This breaks KCSAN's debugfs file
creation, which happened at the end of __init().
There is no reason to create the debugfs file during early
initialization. Therefore, move it into a late_initcall() callback.
Cc: Greg Kroah-Hartman <gregkh(a)linuxfoundation.org>
Cc: "Rafael J. Wysocki" <rafael(a)kernel.org>
Cc: stable <stable(a)vger.kernel.org>
Fixes: 56348560d495 ("debugfs: do not attempt to create a new file before the filesystem is initalized")
Signed-off-by: Marco Elver <elver(a)google.com>
---
I've marked this for 'stable', since 56348560d495 is also intended for
stable, and would subsequently break KCSAN in all stable kernels where
KCSAN is available (since 5.8).
---
kernel/kcsan/core.c | 2 --
kernel/kcsan/debugfs.c | 4 +++-
kernel/kcsan/kcsan.h | 5 -----
3 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/kernel/kcsan/core.c b/kernel/kcsan/core.c
index 8c3867640c21..45c821d4e8bd 100644
--- a/kernel/kcsan/core.c
+++ b/kernel/kcsan/core.c
@@ -644,8 +644,6 @@ void __init kcsan_init(void)
BUG_ON(!in_task());
- kcsan_debugfs_init();
-
for_each_possible_cpu(cpu)
per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles();
diff --git a/kernel/kcsan/debugfs.c b/kernel/kcsan/debugfs.c
index c837ce6c52e6..c1dd02f3be8b 100644
--- a/kernel/kcsan/debugfs.c
+++ b/kernel/kcsan/debugfs.c
@@ -266,7 +266,9 @@ static const struct file_operations debugfs_ops =
.release = single_release
};
-void __init kcsan_debugfs_init(void)
+static void __init kcsan_debugfs_init(void)
{
debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops);
}
+
+late_initcall(kcsan_debugfs_init);
diff --git a/kernel/kcsan/kcsan.h b/kernel/kcsan/kcsan.h
index 594a5dd4842a..9881099d4179 100644
--- a/kernel/kcsan/kcsan.h
+++ b/kernel/kcsan/kcsan.h
@@ -31,11 +31,6 @@ extern bool kcsan_enabled;
void kcsan_save_irqtrace(struct task_struct *task);
void kcsan_restore_irqtrace(struct task_struct *task);
-/*
- * Initialize debugfs file.
- */
-void kcsan_debugfs_init(void);
-
/*
* Statistics counters displayed via debugfs; should only be modified in
* slow-paths.
--
2.30.1.766.gb4fecdf3b7-goog
From: Nadav Amit <namit(a)vmware.com>
Userfaultfd self-test fails occasionally, indicating a memory
corruption.
Analyzing this problem indicates that there is a real bug since
mmap_lock is only taken for read in mwriteprotect_range() and defers
flushes, and since there is insufficient consideration of concurrent
deferred TLB flushes in wp_page_copy(). Although the PTE is flushed from
the TLBs in wp_page_copy(), this flush takes place after the copy has
already been performed, and therefore changes of the page are possible
between the time of the copy and the time in which the PTE is flushed.
To make matters worse, memory-unprotection using userfaultfd also poses
a problem. Although memory unprotection is logically a promotion of PTE
permissions, and therefore should not require a TLB flush, the current
userrfaultfd code might actually cause a demotion of the architectural
PTE permission: when userfaultfd_writeprotect() unprotects memory
region, it unintentionally *clears* the RW-bit if it was already set.
Note that this unprotecting a PTE that is not write-protected is a valid
use-case: the userfaultfd monitor might ask to unprotect a region that
holds both write-protected and write-unprotected PTEs.
The scenario that happens in selftests/vm/userfaultfd is as follows:
cpu0 cpu1 cpu2
---- ---- ----
[ Writable PTE
cached in TLB ]
userfaultfd_writeprotect()
[ write-*unprotect* ]
mwriteprotect_range()
mmap_read_lock()
change_protection()
change_protection_range()
...
change_pte_range()
[ *clear* “write”-bit ]
[ defer TLB flushes ]
[ page-fault ]
...
wp_page_copy()
cow_user_page()
[ copy page ]
[ write to old
page ]
...
set_pte_at_notify()
A similar scenario can happen:
cpu0 cpu1 cpu2 cpu3
---- ---- ---- ----
[ Writable PTE
cached in TLB ]
userfaultfd_writeprotect()
[ write-protect ]
[ deferred TLB flush ]
userfaultfd_writeprotect()
[ write-unprotect ]
[ deferred TLB flush]
[ page-fault ]
wp_page_copy()
cow_user_page()
[ copy page ]
... [ write to page ]
set_pte_at_notify()
This race exists since commit 292924b26024 ("userfaultfd: wp: apply
_PAGE_UFFD_WP bit"). Yet, as Yu Zhao pointed, these races became
apparent since commit 09854ba94c6a ("mm: do_wp_page() simplification")
which made wp_page_copy() more likely to take place, specifically if
page_count(page) > 1.
To resolve the aforementioned races, check whether there are pending
flushes on uffd-write-protected VMAs, and if there are, perform a flush
before doing the COW.
Further optimizations will follow, since currently write-unprotect would
also
Cc: Andrea Arcangeli <aarcange(a)redhat.com>
Cc: Andy Lutomirski <luto(a)kernel.org>
Cc: Peter Xu <peterx(a)redhat.com>
Cc: Pavel Emelyanov <xemul(a)openvz.org>
Cc: Mike Kravetz <mike.kravetz(a)oracle.com>
Cc: Mike Rapoport <rppt(a)linux.vnet.ibm.com>
Cc: Minchan Kim <minchan(a)kernel.org>
Cc: Will Deacon <will(a)kernel.org>
Cc: Peter Zijlstra <peterz(a)infradead.org>
Cc: stable(a)vger.kernel.org
Suggested-by: Yu Zhao <yuzhao(a)google.com>
Fixes: 292924b26024 ("userfaultfd: wp: apply _PAGE_UFFD_WP bit")
Signed-off-by: Nadav Amit <namit(a)vmware.com>
---
v2->v3:
* Do not acquire mmap_lock for write, flush conditionally instead [Yu]
* Change the fixes tag to the patch that made the race apparent [Yu]
* Removing patch to avoid write-protect on uffd unprotect. More
comprehensive solution to follow (and avoid the TLB flush as well).
---
mm/memory.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/mm/memory.c b/mm/memory.c
index 9e8576a83147..06da04f98936 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3092,6 +3092,13 @@ static vm_fault_t do_wp_page(struct vm_fault *vmf)
return handle_userfault(vmf, VM_UFFD_WP);
}
+ /*
+ * Userfaultfd write-protect can defer flushes. Ensure the TLB
+ * is flushed in this case before copying.
+ */
+ if (userfaultfd_wp(vmf->vma) && mm_tlb_flush_pending(vmf->vma->vm_mm))
+ flush_tlb_page(vmf->vma, vmf->address);
+
vmf->page = vm_normal_page(vma, vmf->address, vmf->orig_pte);
if (!vmf->page) {
/*
--
2.25.1
ALSA: hda/realtek: Add quirk for Intel NUC 10
This adds a new SND_PCI_QUIRK(...) and applies it to the Intel NUC 10
devices. This fixes the issue of the devices not having audio input and
output on the headset jack because the kernel does not recognize when
something is plugged in.
The new quirk was inspired by the quirk for the Intel NUC 8 devices, but
it turned out that the NUC 10 uses another pin. This information was
acquired by black box testing likely pins.
Co-developed-by: Eckhart Mohr <e.mohr(a)tuxedocomputers.com>
Signed-off-by: Eckhart Mohr <e.mohr(a)tuxedocomputers.com>
Signed-off-by: Werner Sembach <wse(a)tuxedocomputers.com>
Cc: <stable(a)vger.kernel.org>
---
Resend of this patch with git send-email, because last patch got tabs replaced with spaces.
>From d281364b8ca6c054a0e5ce20caa599bf7d08160d Mon Sep 17 00:00:00 2001
From: Werner Sembach <wse(a)tuxedocomputers.com>
Date: Fri, 26 Feb 2021 13:54:30 +0100
Subject: [PATCH] Fix Intel NUC10 no output and input on headset jack
---
sound/pci/hda/patch_realtek.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 290645516313..c14d624dbaf1 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6362,6 +6362,7 @@ enum {
ALC269_FIXUP_LEMOTE_A1802,
ALC269_FIXUP_LEMOTE_A190X,
ALC256_FIXUP_INTEL_NUC8_RUGGED,
+ ALC256_FIXUP_INTEL_NUC10,
ALC255_FIXUP_XIAOMI_HEADSET_MIC,
ALC274_FIXUP_HP_MIC,
ALC274_FIXUP_HP_HEADSET_MIC,
@@ -7744,6 +7745,15 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE
},
+ [ALC256_FIXUP_INTEL_NUC10] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
[ALC255_FIXUP_XIAOMI_HEADSET_MIC] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -8183,6 +8193,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1c06, 0x2013, "Lemote A1802", ALC269_FIXUP_LEMOTE_A1802),
SND_PCI_QUIRK(0x1c06, 0x2015, "Lemote A190X", ALC269_FIXUP_LEMOTE_A190X),
SND_PCI_QUIRK(0x8086, 0x2080, "Intel NUC 8 Rugged", ALC256_FIXUP_INTEL_NUC8_RUGGED),
+ SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", ALC256_FIXUP_INTEL_NUC10),
#if 0
/* Below is a quirk table taken from the old code.
--
2.25.1
From: "Steven J. Magnani" <magnani(a)ieee.org>
[ Upstream commit 63c9e47a1642fc817654a1bc18a6ec4bbcc0f056 ]
When extending a file, udf_do_extend_file() may enter following empty
indirect extent. At the end of udf_do_extend_file() we revert prev_epos
to point to the last written extent. However if we end up not adding any
further extent in udf_do_extend_file(), the reverting points prev_epos
into the header area of the AED and following updates of the extents
(in udf_update_extents()) will corrupt the header.
Make sure that we do not follow indirect extent if we are not going to
add any more extents so that returning back to the last written extent
works correctly.
Link: https://lore.kernel.org/r/20210107234116.6190-2-magnani@ieee.org
Signed-off-by: Steven J. Magnani <magnani(a)ieee.org>
Signed-off-by: Jan Kara <jack(a)suse.cz>
Signed-off-by: Sasha Levin <sashal(a)kernel.org>
---
fs/udf/inode.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 149baf5f3d19..50607673a6a9 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -548,11 +548,14 @@ static int udf_do_extend_file(struct inode *inode,
udf_write_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1);
+
/*
- * We've rewritten the last extent but there may be empty
- * indirect extent after it - enter it.
+ * We've rewritten the last extent. If we are going to add
+ * more extents, we may need to enter possible following
+ * empty indirect extent.
*/
- udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
+ if (new_block_bytes || prealloc_len)
+ udf_next_aext(inode, last_pos, &tmploc, &tmplen, 0);
}
/* Managed to do everything necessary? */
--
2.30.1