[Intel-gfx] [PATCH] drm/i915: Rework sdvo proxy i2c locking
Chris Wilson
chris at chris-wilson.co.uk
Wed Jul 26 09:47:17 UTC 2017
Quoting Daniel Vetter (2017-07-26 07:02:44)
> lockdep complaints about a locking recursion for the i2c bus lock
> because both the sdvo ddc proxy bus and the gmbus nested within use
> the same locking class. It's not really a deadlock since we never nest
> the other way round, but it's annoying.
>
> Fix it by pulling the gmbus locking into the i2c lock_ops for both
> i2c_adapater and making sure that the ddc_proxy_xfer function is
> entirely lockless.
>
> Re-layouting the extracted function resulted in some whitespace
> cleanups, I figured we might as well keep them.
>
> Signed-off-by: Daniel Vetter <daniel.vetter at intel.com>
> ---
> diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
> index 0403e30dfabc..392d3d2d4bbf 100644
> --- a/drivers/gpu/drm/i915/intel_sdvo.c
> +++ b/drivers/gpu/drm/i915/intel_sdvo.c
> @@ -451,23 +451,24 @@ static const char * const cmd_status_names[] = {
> "Scaling not supported"
> };
>
> -static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
> - const void *args, int args_len)
> +static bool __intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
> + const void *args, int args_len,
> + bool locked)
> {
> u8 *buf, status;
> struct i2c_msg *msgs;
> int i, ret = true;
>
> - /* Would be simpler to allocate both in one go ? */
> + /* Would be simpler to allocate both in one go ? */
> buf = kzalloc(args_len * 2 + 2, GFP_KERNEL);
> if (!buf)
> return false;
>
> msgs = kcalloc(args_len + 3, sizeof(*msgs), GFP_KERNEL);
> if (!msgs) {
> - kfree(buf);
> + kfree(buf);
> return false;
> - }
> + }
>
> intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
>
> @@ -498,7 +499,10 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
> msgs[i+2].len = 1;
> msgs[i+2].buf = &status;
>
> - ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
> + if (locked)
locked=true is called from the unlocked context, very confusing.
> + ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
> + else
> + ret = __i2c_transfer(intel_sdvo->i2c, msgs, i+3);
> if (ret < 0) {
> DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
> ret = false;
> @@ -516,6 +520,12 @@ static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
> return ret;
> }
>
> +static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
> + const void *args, int args_len)
> +{
> + return __intel_sdvo_write_cmd(intel_sdvo, cmd, args, args_len, true);
> +}
> +
> static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
> void *response, int response_len)
> {
> @@ -602,13 +612,13 @@ static int intel_sdvo_get_pixel_multiplier(const struct drm_display_mode *adjust
> return 4;
> }
>
> -static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
> - u8 ddc_bus)
> +static bool __intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
> + u8 ddc_bus)
> {
> /* This must be the immediately preceding write before the i2c xfer */
> - return intel_sdvo_write_cmd(intel_sdvo,
> - SDVO_CMD_SET_CONTROL_BUS_SWITCH,
> - &ddc_bus, 1);
> + return __intel_sdvo_write_cmd(intel_sdvo,
> + SDVO_CMD_SET_CONTROL_BUS_SWITCH,
> + &ddc_bus, 1, false);
> }
>
> static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len)
> @@ -2934,7 +2944,7 @@ static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
> {
> struct intel_sdvo *sdvo = adapter->algo_data;
>
> - if (!intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
> + if (!__intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
> return -EIO;
>
> return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
> @@ -2951,6 +2961,39 @@ static const struct i2c_algorithm intel_sdvo_ddc_proxy = {
> .functionality = intel_sdvo_ddc_proxy_func
> };
>
> +static void proxy_lock_bus(struct i2c_adapter *adapter,
> + unsigned int flags)
> +{
> + struct intel_sdvo *sdvo = adapter->algo_data;
> + struct drm_i915_private *dev_priv = to_i915(sdvo->base.base.dev);
> +
> + mutex_lock(&dev_priv->gmbus_mutex);
sdvo->i2c->lock_ops->lock_bus(sdvo->i2c, flags);
> +}
> +
> +static int proxy_trylock_bus(struct i2c_adapter *adapter,
> + unsigned int flags)
> +{
> + struct intel_sdvo *sdvo = adapter->algo_data;
> + struct drm_i915_private *dev_priv = to_i915(sdvo->base.base.dev);
> +
> + return mutex_trylock(&dev_priv->gmbus_mutex);
sdvo->i2c->lock_ops->trylock_bus(sdvo->i2c, flags);
> +}
> +
> +static void proxy_unlock_bus(struct i2c_adapter *adapter,
> + unsigned int flags)
> +{
> + struct intel_sdvo *sdvo = adapter->algo_data;
> + struct drm_i915_private *dev_priv = to_i915(sdvo->base.base.dev);
> +
> + mutex_unlock(&dev_priv->gmbus_mutex);
sdvo->i2c->lock_ops->unlock_bus(sdvo->i2c, flags);
On reflection, you only need to hook up gmbus.lock_ops since gmbus is a
different lockclass it will be allowed to nest.
-Chris
More information about the Intel-gfx
mailing list