@@ -1877,17 +1877,19 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
struct drm_i915_private *dev_priv = dev->dev_private;
ifbdev = dev_priv->fbdev;
- fb = to_intel_framebuffer(ifbdev->helper.fb);
-
- seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
- fb->base.width,
- fb->base.height,
- fb->base.depth,
- fb->base.bits_per_pixel,
- fb->base.modifier[0],
- atomic_read(&fb->base.refcount.refcount));
- describe_obj(m, fb->obj);
- seq_putc(m, '\n');
+ if (ifbdev && ifbdev->helper.fb && ifbdev->fb->obj) {
+ fb = to_intel_framebuffer(ifbdev->helper.fb);
+
+ seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
+ fb->base.width,
+ fb->base.height,
+ fb->base.depth,
+ fb->base.bits_per_pixel,
+ fb->base.modifier[0],
+ atomic_read(&fb->base.refcount.refcount));
+ describe_obj(m, fb->obj);
+ seq_putc(m, '\n');
+ }
#endif
mutex_lock(&dev->mode_config.fb_lock);
@@ -414,7 +414,10 @@ static void intel_connector_add_to_fbdev(struct intel_connector *connector)
{
#ifdef CONFIG_DRM_FBDEV_EMULATION
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, &connector->base);
+
+ if (dev_priv->fbdev)
+ drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper,
+ &connector->base);
#endif
}
@@ -422,7 +425,10 @@ static void intel_connector_remove_from_fbdev(struct intel_connector *connector)
{
#ifdef CONFIG_DRM_FBDEV_EMULATION
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
- drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, &connector->base);
+
+ if (dev_priv->fbdev)
+ drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper,
+ &connector->base);
#endif
}
@@ -529,8 +529,10 @@ static void intel_fbdev_destroy(struct drm_device *dev,
drm_fb_helper_fini(&ifbdev->helper);
- drm_framebuffer_unregister_private(&ifbdev->fb->base);
- drm_framebuffer_remove(&ifbdev->fb->base);
+ if (ifbdev->fb) {
+ drm_framebuffer_unregister_private(&ifbdev->fb->base);
+ drm_framebuffer_remove(&ifbdev->fb->base);
+ }
}
/*
@@ -734,7 +736,7 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
struct intel_fbdev *ifbdev = dev_priv->fbdev;
struct fb_info *info;
- if (!ifbdev)
+ if (!ifbdev || !ifbdev->fb || !ifbdev->fb->obj || !ifbdev->helper.fbdev)
return;
info = ifbdev->helper.fbdev;
@@ -780,8 +782,10 @@ void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous
void intel_fbdev_output_poll_changed(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (dev_priv->fbdev)
- drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
+ struct intel_fbdev *ifbdev = dev_priv->fbdev;
+
+ if (ifbdev && ifbdev->fb && ifbdev->fb->obj && ifbdev->helper.fbdev)
+ drm_fb_helper_hotplug_event(&ifbdev->helper);
}
void intel_fbdev_restore_mode(struct drm_device *dev)
@@ -791,7 +795,7 @@ void intel_fbdev_restore_mode(struct drm_device *dev)
struct intel_fbdev *ifbdev = dev_priv->fbdev;
struct drm_fb_helper *fb_helper;
- if (!ifbdev)
+ if (!ifbdev || !ifbdev->fb || !ifbdev->fb->obj || !ifbdev->helper.fbdev)
return;
fb_helper = &ifbdev->helper;
intelfb_create() is called once on driver initialization. If it fails, ifbdev->helper.fbdev, ifbdev->fb or ifbdev->fb->obj may be NULL. intel_fbdev_destroy() is called on driver unload and dereferences ifbdev->fb. intel_fbdev_set_suspend() is called on suspend/resume and dereferences ifbdev->helper.fbdev and ifbdev->fb->obj. intel_fbdev_output_poll_changed() is called on hotplug events and calls drm_fb_helper_hotplug_event(). If ifbdev->helper.fb is NULL it will signal a delayed_hotplug ad infinitum; if it is not NULL it will call drm_fb_helper_set_par() which dereferences ifbdev->helper.fbdev. intel_fbdev_restore_mode() is called on lastclose and dereferences ifbdev->fb->obj. It also calls __drm_atomic_helper_set_config() which WARNs if ifbdev->fb is NULL, and drm_fb_helper_set_par() which dereferences ifbdev->helper.fbdev. i915_gem_framebuffer_info() is called on access to debugfs file "i915_gem_framebuffer" and dereferences ifbdev, ifbdev->helper.fb and ifbdev->helper.fb->obj. intel_connector_add_to_fbdev() / intel_connector_remove_from_fbdev() are called when registering / unregistering an mst connector and dereference ifbdev. Basically if fbdev creation fails, we end up with an ifbdev that's only partially initialized and the driver will oops on unload, the next suspend/resume cycle, hotplug events, when X11 is stopped or when accessing debugfs file "i915_gem_framebuffer". After the fbdev was destroyed with intel_fbdev_fini(), the driver will oops on mst hotplug events. Fix it. Signed-off-by: Lukas Wunner <lukas@wunner.de> --- drivers/gpu/drm/i915/i915_debugfs.c | 24 +++++++++++++----------- drivers/gpu/drm/i915/intel_dp_mst.c | 10 ++++++++-- drivers/gpu/drm/i915/intel_fbdev.c | 16 ++++++++++------ 3 files changed, 31 insertions(+), 19 deletions(-)