mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 00:51:51 -04:00
Merge tag 'drm-misc-fixes-2026-04-02' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes
A refcounting fix for bridges, revert a previous framebuffer use-after-free fix that turned out to be causing more problems, a hang fix for qaic, an initialization fix for ast, a error handling fix for sysfb, and a speculation fix for drm_compat_ioctl. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maxime Ripard <mripard@redhat.com> Link: https://patch.msgid.link/20260402-vivid-perfect-caiman-ca055e@houat
This commit is contained in:
@@ -914,7 +914,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
|
||||
*/
|
||||
return -ENODEV;
|
||||
|
||||
if (status) {
|
||||
if (usr && status) {
|
||||
/*
|
||||
* Releasing resources failed on the device side, which puts
|
||||
* us in a bind since they may still be in use, so enable the
|
||||
@@ -1109,6 +1109,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
|
||||
mutex_lock(&qdev->cntl_mutex);
|
||||
if (!list_empty(&elem.list))
|
||||
list_del(&elem.list);
|
||||
/* resp_worker() processed the response but the wait was interrupted */
|
||||
else if (ret == -ERESTARTSYS)
|
||||
ret = 0;
|
||||
if (!ret && !elem.buf)
|
||||
ret = -ETIMEDOUT;
|
||||
else if (ret > 0 && !elem.buf)
|
||||
@@ -1419,9 +1422,49 @@ static void resp_worker(struct work_struct *work)
|
||||
}
|
||||
mutex_unlock(&qdev->cntl_mutex);
|
||||
|
||||
if (!found)
|
||||
if (!found) {
|
||||
/*
|
||||
* The user might have gone away at this point without waiting
|
||||
* for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from
|
||||
* the device. If this is not handled correctly, the host will
|
||||
* not know that the DBC[n] has been freed on the device.
|
||||
* Due to this failure in synchronization between the device and
|
||||
* the host, if another user requests to activate a network, and
|
||||
* the device assigns DBC[n] again, save_dbc_buf() will hang,
|
||||
* waiting for dbc[n]->in_use to be set to false, which will not
|
||||
* happen unless the qaic_dev_reset_clean_local_state() gets
|
||||
* called by resetting the device (or re-inserting the module).
|
||||
*
|
||||
* As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV
|
||||
* transactions in the message before disposing of it, then
|
||||
* handle releasing the DBC resources.
|
||||
*
|
||||
* Since the user has gone away, if the device could not
|
||||
* deactivate the network (status != 0), there is no way to
|
||||
* enable and reassign the DBC to the user. We can put trust in
|
||||
* the device that it will release all the active DBCs in
|
||||
* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
|
||||
* otherwise, the user can issue an soc_reset to the device.
|
||||
*/
|
||||
u32 msg_count = le32_to_cpu(msg->hdr.count);
|
||||
u32 msg_len = le32_to_cpu(msg->hdr.len);
|
||||
u32 len = 0;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < msg_count && len < msg_len; ++j) {
|
||||
struct wire_trans_hdr *trans_hdr;
|
||||
|
||||
trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
|
||||
if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
|
||||
if (decode_deactivate(qdev, trans_hdr, &len, NULL))
|
||||
len += le32_to_cpu(trans_hdr->len);
|
||||
} else {
|
||||
len += le32_to_cpu(trans_hdr->len);
|
||||
}
|
||||
}
|
||||
/* request must have timed out, drop packet */
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
kfree(resp);
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ static void ast_init_analog(struct ast_device *ast)
|
||||
/* Finally, clear bits [17:16] of SCU2c */
|
||||
data = ast_read32(ast, 0x1202c);
|
||||
data &= 0xfffcffff;
|
||||
ast_write32(ast, 0, data);
|
||||
ast_write32(ast, 0x1202c, data);
|
||||
|
||||
/* Disable DVO */
|
||||
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00);
|
||||
|
||||
@@ -1569,11 +1569,17 @@ EXPORT_SYMBOL(devm_drm_put_bridge);
|
||||
static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
|
||||
struct drm_bridge *bridge,
|
||||
unsigned int idx,
|
||||
bool lingering)
|
||||
bool lingering,
|
||||
bool scoped)
|
||||
{
|
||||
unsigned int refcount = kref_read(&bridge->refcount);
|
||||
|
||||
if (scoped)
|
||||
refcount--;
|
||||
|
||||
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
|
||||
|
||||
drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount),
|
||||
drm_printf(p, "\trefcount: %u%s\n", refcount,
|
||||
lingering ? " [lingering]" : "");
|
||||
|
||||
drm_printf(p, "\ttype: [%d] %s\n",
|
||||
@@ -1607,10 +1613,10 @@ static int allbridges_show(struct seq_file *m, void *data)
|
||||
mutex_lock(&bridge_lock);
|
||||
|
||||
list_for_each_entry(bridge, &bridge_list, list)
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, false);
|
||||
|
||||
list_for_each_entry(bridge, &bridge_lingering_list, list)
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true);
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true, false);
|
||||
|
||||
mutex_unlock(&bridge_lock);
|
||||
|
||||
@@ -1625,7 +1631,7 @@ static int encoder_bridges_show(struct seq_file *m, void *data)
|
||||
unsigned int idx = 0;
|
||||
|
||||
drm_for_each_bridge_in_chain_scoped(encoder, bridge)
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
|
||||
drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,6 @@ static void drm_events_release(struct drm_file *file_priv)
|
||||
void drm_file_free(struct drm_file *file)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
int idx;
|
||||
|
||||
if (!file)
|
||||
return;
|
||||
@@ -250,11 +249,9 @@ void drm_file_free(struct drm_file *file)
|
||||
|
||||
drm_events_release(file);
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET) &&
|
||||
drm_dev_enter(dev, &idx)) {
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
drm_fb_release(file);
|
||||
drm_property_destroy_user_blobs(dev, file);
|
||||
drm_dev_exit(idx);
|
||||
}
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#include <linux/compat.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
@@ -374,6 +375,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
if (nr >= ARRAY_SIZE(drm_compat_ioctls))
|
||||
return drm_ioctl(filp, cmd, arg);
|
||||
|
||||
nr = array_index_nospec(nr, ARRAY_SIZE(drm_compat_ioctls));
|
||||
fn = drm_compat_ioctls[nr].fn;
|
||||
if (!fn)
|
||||
return drm_ioctl(filp, cmd, arg);
|
||||
|
||||
@@ -577,13 +577,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
||||
*/
|
||||
WARN_ON(!list_empty(&dev->mode_config.fb_list));
|
||||
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
|
||||
if (list_empty(&fb->filp_head) || drm_framebuffer_read_refcount(fb) > 1) {
|
||||
struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]");
|
||||
struct drm_printer p = drm_dbg_printer(dev, DRM_UT_KMS, "[leaked fb]");
|
||||
|
||||
drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
|
||||
drm_framebuffer_print_info(&p, 1, fb);
|
||||
}
|
||||
list_del_init(&fb->filp_head);
|
||||
drm_printf(&p, "framebuffer[%u]:\n", fb->base.id);
|
||||
drm_framebuffer_print_info(&p, 1, fb);
|
||||
drm_framebuffer_free(&fb->base.refcount);
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,6 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
|
||||
struct drm_sysfb_device *sysfb;
|
||||
struct drm_device *dev;
|
||||
struct resource *mem = NULL;
|
||||
void __iomem *screen_base = NULL;
|
||||
struct drm_plane *primary_plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_encoder *encoder;
|
||||
@@ -238,21 +237,38 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
|
||||
|
||||
mem_flags = efidrm_get_mem_flags(dev, res->start, vsize);
|
||||
|
||||
if (mem_flags & EFI_MEMORY_WC)
|
||||
screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem));
|
||||
else if (mem_flags & EFI_MEMORY_UC)
|
||||
screen_base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
|
||||
else if (mem_flags & EFI_MEMORY_WT)
|
||||
screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
|
||||
MEMREMAP_WT);
|
||||
else if (mem_flags & EFI_MEMORY_WB)
|
||||
screen_base = devm_memremap(&pdev->dev, mem->start, resource_size(mem),
|
||||
MEMREMAP_WB);
|
||||
else
|
||||
if (mem_flags & EFI_MEMORY_WC) {
|
||||
void __iomem *screen_base = devm_ioremap_wc(&pdev->dev, mem->start,
|
||||
resource_size(mem));
|
||||
|
||||
if (!screen_base)
|
||||
return ERR_PTR(-ENXIO);
|
||||
iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
|
||||
} else if (mem_flags & EFI_MEMORY_UC) {
|
||||
void __iomem *screen_base = devm_ioremap(&pdev->dev, mem->start,
|
||||
resource_size(mem));
|
||||
|
||||
if (!screen_base)
|
||||
return ERR_PTR(-ENXIO);
|
||||
iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
|
||||
} else if (mem_flags & EFI_MEMORY_WT) {
|
||||
void *screen_base = devm_memremap(&pdev->dev, mem->start,
|
||||
resource_size(mem), MEMREMAP_WT);
|
||||
|
||||
if (IS_ERR(screen_base))
|
||||
return ERR_CAST(screen_base);
|
||||
iosys_map_set_vaddr(&sysfb->fb_addr, screen_base);
|
||||
} else if (mem_flags & EFI_MEMORY_WB) {
|
||||
void *screen_base = devm_memremap(&pdev->dev, mem->start,
|
||||
resource_size(mem), MEMREMAP_WB);
|
||||
|
||||
if (IS_ERR(screen_base))
|
||||
return ERR_CAST(screen_base);
|
||||
iosys_map_set_vaddr(&sysfb->fb_addr, screen_base);
|
||||
} else {
|
||||
drm_err(dev, "invalid mem_flags: 0x%llx\n", mem_flags);
|
||||
if (!screen_base)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Modesetting
|
||||
|
||||
Reference in New Issue
Block a user