[Intel-gfx] [PATCH v3] drm/i915: Disable HPD interrupt on pin when irq storm is detected (v3)
This patch disables hotplug interrupts if an 'interrupt storm' has been detected. Noise on the interrupt line renders the hotplug interrupt useless: each hotplug event causes the devices to be rescanned which will will only increase the system load. Thus disable the hotplug interrupts and fall back to periodic device polling. v2: Fixed cleanup typo. v3: Fixed format issues, clarified a variable name, changed pr_warn() to DRM_INFO() as suggested by Jani Nikula jani.nik...@linux.intel.com. Signed-off-by: Egbert Eich e...@suse.de --- drivers/gpu/drm/i915/i915_irq.c | 75 +++-- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a3f1ac4..834c717 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -88,7 +88,8 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS }; - +static void ibx_hpd_irq_setup(struct drm_device *dev); +static void i915_hpd_irq_setup(struct drm_device *dev); /* For display hotplug interrupt */ static void @@ -342,7 +343,11 @@ static void i915_hotplug_work_func(struct work_struct *work) hotplug_work); struct drm_device *dev = dev_priv-dev; struct drm_mode_config *mode_config = dev-mode_config; - struct intel_encoder *encoder; + struct intel_connector *intel_connector; + struct intel_encoder *intel_encoder; + struct drm_connector *connector; + unsigned long irqflags; + bool hpd_disabled = false; /* HPD irq before everything is fully set up. */ if (!dev_priv-enable_hotplug_processing) @@ -351,9 +356,33 @@ static void i915_hotplug_work_func(struct work_struct *work) mutex_lock(mode_config-mutex); DRM_DEBUG_KMS(running encoder hotplug functions\n); - list_for_each_entry(encoder, mode_config-encoder_list, base.head) - if (encoder-hot_plug) - encoder-hot_plug(encoder); + spin_lock_irqsave(dev_priv-irq_lock, irqflags); + list_for_each_entry(connector, mode_config-connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector-encoder; + if (intel_encoder-hpd_pin HPD_NONE + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark == HPD_MARK_DISABLED + connector-polled == DRM_CONNECTOR_POLL_HPD) { + DRM_INFO(HPD interrupt storm detected on connector %s: +switching from hotplug detection to polling\n, + drm_get_connector_name(connector)); + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark = HPD_DISABLED; + connector-polled = DRM_CONNECTOR_POLL_CONNECT + | DRM_CONNECTOR_POLL_DISCONNECT; + hpd_disabled = true; + } + } +/* if there were no outputs to poll, poll was disabled, + * therefore make sure it's enabled when disabling HPD on + * some connectors */ + if (hpd_disabled) { + drm_kms_helper_poll_enable(dev); + } + spin_unlock_irqrestore(dev_priv-irq_lock, irqflags); + + list_for_each_entry(intel_encoder, mode_config-encoder_list, base.head) + if (intel_encoder-hot_plug) + intel_encoder-hot_plug(intel_encoder); mutex_unlock(mode_config-mutex); @@ -584,13 +613,14 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, #define HPD_STORM_DETECT_PERIOD 1000 -static inline void hotplug_irq_storm_detect(struct drm_device *dev, +static inline bool hotplug_irq_storm_detect(struct drm_device *dev, u32 hotplug_trigger, const u32 *hpd) { drm_i915_private_t *dev_priv = dev-dev_private; unsigned long irqflags; int i; + bool ret = false; spin_lock_irqsave(dev_priv-irq_lock, irqflags); @@ -605,12 +635,15 @@ static inline void hotplug_irq_storm_detect(struct drm_device *dev, } else if (dev_priv-hpd_stats[i].hpd_cnt 5) { dev_priv-hpd_stats[i].hpd_mark = HPD_MARK_DISABLED; DRM_DEBUG_KMS(HPD interrupt storm detected on PIN %d\n, i); + ret = true; } else dev_priv-hpd_stats[i].hpd_cnt++; } } spin_unlock_irqrestore(dev_priv-irq_lock, irqflags); + + return ret; } static void gmbus_irq_handler(struct drm_device *dev) @@ -686,7 +719,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void
Re: [Intel-gfx] [PATCH v3] drm/i915: Disable HPD interrupt on pin when irq storm is detected (v3)
On Thu, 11 Apr 2013, Egbert Eich e...@suse.de wrote: This patch disables hotplug interrupts if an 'interrupt storm' has been detected. Noise on the interrupt line renders the hotplug interrupt useless: each hotplug event causes the devices to be rescanned which will will only increase the system load. Thus disable the hotplug interrupts and fall back to periodic device polling. v2: Fixed cleanup typo. v3: Fixed format issues, clarified a variable name, changed pr_warn() to DRM_INFO() as suggested by Jani Nikula jani.nik...@linux.intel.com. Signed-off-by: Egbert Eich e...@suse.de --- drivers/gpu/drm/i915/i915_irq.c | 75 +++-- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a3f1ac4..834c717 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -88,7 +88,8 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview are the same */ [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS }; - +static void ibx_hpd_irq_setup(struct drm_device *dev); +static void i915_hpd_irq_setup(struct drm_device *dev); /* For display hotplug interrupt */ static void @@ -342,7 +343,11 @@ static void i915_hotplug_work_func(struct work_struct *work) hotplug_work); struct drm_device *dev = dev_priv-dev; struct drm_mode_config *mode_config = dev-mode_config; - struct intel_encoder *encoder; + struct intel_connector *intel_connector; + struct intel_encoder *intel_encoder; + struct drm_connector *connector; + unsigned long irqflags; + bool hpd_disabled = false; /* HPD irq before everything is fully set up. */ if (!dev_priv-enable_hotplug_processing) @@ -351,9 +356,33 @@ static void i915_hotplug_work_func(struct work_struct *work) mutex_lock(mode_config-mutex); DRM_DEBUG_KMS(running encoder hotplug functions\n); - list_for_each_entry(encoder, mode_config-encoder_list, base.head) - if (encoder-hot_plug) - encoder-hot_plug(encoder); + spin_lock_irqsave(dev_priv-irq_lock, irqflags); + list_for_each_entry(connector, mode_config-connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector-encoder; + if (intel_encoder-hpd_pin HPD_NONE + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark == HPD_MARK_DISABLED + connector-polled == DRM_CONNECTOR_POLL_HPD) { + DRM_INFO(HPD interrupt storm detected on connector %s: + switching from hotplug detection to polling\n, + drm_get_connector_name(connector)); + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark = HPD_DISABLED; + connector-polled = DRM_CONNECTOR_POLL_CONNECT + | DRM_CONNECTOR_POLL_DISCONNECT; + hpd_disabled = true; + } + } + /* if there were no outputs to poll, poll was disabled, + * therefore make sure it's enabled when disabling HPD on + * some connectors */ + if (hpd_disabled) { + drm_kms_helper_poll_enable(dev); + } + spin_unlock_irqrestore(dev_priv-irq_lock, irqflags); + + list_for_each_entry(intel_encoder, mode_config-encoder_list, base.head) + if (intel_encoder-hot_plug) + intel_encoder-hot_plug(intel_encoder); mutex_unlock(mode_config-mutex); @@ -584,13 +613,14 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, #define HPD_STORM_DETECT_PERIOD 1000 -static inline void hotplug_irq_storm_detect(struct drm_device *dev, +static inline bool hotplug_irq_storm_detect(struct drm_device *dev, u32 hotplug_trigger, const u32 *hpd) { drm_i915_private_t *dev_priv = dev-dev_private; unsigned long irqflags; int i; + bool ret = false; spin_lock_irqsave(dev_priv-irq_lock, irqflags); @@ -605,12 +635,15 @@ static inline void hotplug_irq_storm_detect(struct drm_device *dev, } else if (dev_priv-hpd_stats[i].hpd_cnt 5) { dev_priv-hpd_stats[i].hpd_mark = HPD_MARK_DISABLED; DRM_DEBUG_KMS(HPD interrupt storm detected on PIN %d\n, i); + ret = true; } else dev_priv-hpd_stats[i].hpd_cnt++; } } spin_unlock_irqrestore(dev_priv-irq_lock, irqflags); + + return ret; } static void gmbus_irq_handler(struct drm_device *dev) @@ -686,7 +719,8 @@