diff mbox

drm: refcnt drm_display_mode

Message ID 1406328670-1186-1-git-send-email-robdclark@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rob Clark July 25, 2014, 10:51 p.m. UTC
We're going to need this for atomic.

Signed-off-by: Rob Clark <robdclark@gmail.com>
---
 drivers/gpu/drm/drm_crtc.c                    |  4 +--
 drivers/gpu/drm/drm_crtc_helper.c             |  2 +-
 drivers/gpu/drm/drm_edid.c                    |  6 ++--
 drivers/gpu/drm/drm_fb_helper.c               |  6 ++--
 drivers/gpu/drm/drm_modes.c                   | 41 ++++++++++++++++++++-------
 drivers/gpu/drm/exynos/exynos_drm_connector.c |  2 +-
 drivers/gpu/drm/gma500/psb_intel_sdvo.c       |  3 +-
 drivers/gpu/drm/i915/intel_panel.c            |  8 ++----
 drivers/gpu/drm/i915/intel_sdvo.c             |  3 +-
 drivers/gpu/drm/nouveau/nouveau_connector.c   |  2 +-
 drivers/gpu/drm/omapdrm/omap_connector.c      |  2 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c           |  4 +--
 include/drm/drm_modes.h                       |  5 +++-
 13 files changed, 52 insertions(+), 36 deletions(-)

Comments

Daniel Vetter July 27, 2014, 3:17 p.m. UTC | #1
On Sat, Jul 26, 2014 at 12:51 AM, Rob Clark <robdclark@gmail.com> wrote:
> We're going to need this for atomic.
>
> Signed-off-by: Rob Clark <robdclark@gmail.com>

I disagree. Iiui correctly Rob's concern is that the additional stuff
to keep track of mode lists (list_head and the idr stuff) could
confuse driver writers into doing stupid stuff when they embed
drm_display_mode into some other stuff. Imo the right fix would be to
just remove them (but that's fairly invasive to the mode list code).

Now wrt atomic we only need refcounting because currently
drm_atomic_state is refcounted. And we don't need that afaics (and I'm
working on the draft code to show how). So without a clear need for
refcounting I really prefer we don't add this complexity - doing
refcounting for fbs wasn't fun at all ;-)
-Daniel
> ---
>  drivers/gpu/drm/drm_crtc.c                    |  4 +--
>  drivers/gpu/drm/drm_crtc_helper.c             |  2 +-
>  drivers/gpu/drm/drm_edid.c                    |  6 ++--
>  drivers/gpu/drm/drm_fb_helper.c               |  6 ++--
>  drivers/gpu/drm/drm_modes.c                   | 41 ++++++++++++++++++++-------
>  drivers/gpu/drm/exynos/exynos_drm_connector.c |  2 +-
>  drivers/gpu/drm/gma500/psb_intel_sdvo.c       |  3 +-
>  drivers/gpu/drm/i915/intel_panel.c            |  8 ++----
>  drivers/gpu/drm/i915/intel_sdvo.c             |  3 +-
>  drivers/gpu/drm/nouveau/nouveau_connector.c   |  2 +-
>  drivers/gpu/drm/omapdrm/omap_connector.c      |  2 +-
>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c           |  4 +--
>  include/drm/drm_modes.h                       |  5 +++-
>  13 files changed, 52 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 1ccf5cb..7a7fced 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -822,7 +822,7 @@ static void drm_mode_remove(struct drm_connector *connector,
>                             struct drm_display_mode *mode)
>  {
>         list_del(&mode->head);
> -       drm_mode_destroy(connector->dev, mode);
> +       drm_mode_unreference(mode);
>  }
>
>  /**
> @@ -2602,7 +2602,7 @@ out:
>                 drm_framebuffer_unreference(fb);
>
>         kfree(connector_set);
> -       drm_mode_destroy(dev, mode);
> +       drm_mode_unreference(mode);
>         drm_modeset_unlock_all(dev);
>         return ret;
>  }
> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
> index 6c65a0a..757de8b 100644
> --- a/drivers/gpu/drm/drm_crtc_helper.c
> +++ b/drivers/gpu/drm/drm_crtc_helper.c
> @@ -384,7 +384,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>
>         /* FIXME: add subpixel order */
>  done:
> -       drm_mode_destroy(dev, adjusted_mode);
> +       drm_mode_unreference(adjusted_mode);
>         if (!ret) {
>                 crtc->enabled = saved_enabled;
>                 crtc->mode = saved_mode;
> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
> index 087d608..cbc021d 100644
> --- a/drivers/gpu/drm/drm_edid.c
> +++ b/drivers/gpu/drm/drm_edid.c
> @@ -1705,7 +1705,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
>                 if (!mode)
>                         return NULL;
>                 if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
> -                       drm_mode_destroy(dev, mode);
> +                       drm_mode_unreference(mode);
>                         mode = drm_gtf_mode_complex(dev, hsize, vsize,
>                                                     vrefresh_rate, 0, 0,
>                                                     drm_gtf2_m(edid),
> @@ -2021,7 +2021,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
>                 fixup_mode_1366x768(newmode);
>                 if (!mode_in_range(newmode, edid, timing) ||
>                     !valid_inferred_mode(connector, newmode)) {
> -                       drm_mode_destroy(dev, newmode);
> +                       drm_mode_unreference(newmode);
>                         continue;
>                 }
>
> @@ -2050,7 +2050,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
>                 fixup_mode_1366x768(newmode);
>                 if (!mode_in_range(newmode, edid, timing) ||
>                     !valid_inferred_mode(connector, newmode)) {
> -                       drm_mode_destroy(dev, newmode);
> +                       drm_mode_unreference(newmode);
>                         continue;
>                 }
>
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 3144db9..5c96a6a 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -588,7 +588,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>         for (i = 0; i < helper->crtc_count; i++) {
>                 kfree(helper->crtc_info[i].mode_set.connectors);
>                 if (helper->crtc_info[i].mode_set.mode)
> -                       drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
> +                       drm_mode_unreference(helper->crtc_info[i].mode_set.mode);
>         }
>         kfree(helper->crtc_info);
>  }
> @@ -1606,7 +1606,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
>                                       mode->name, fb_crtc->mode_set.crtc->base.id);
>                         fb_crtc->desired_mode = mode;
>                         if (modeset->mode)
> -                               drm_mode_destroy(dev, modeset->mode);
> +                               drm_mode_unreference(modeset->mode);
>                         modeset->mode = drm_mode_duplicate(dev,
>                                                            fb_crtc->desired_mode);
>                         modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
> @@ -1621,7 +1621,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
>                         BUG_ON(modeset->fb);
>                         BUG_ON(modeset->num_connectors);
>                         if (modeset->mode)
> -                               drm_mode_destroy(dev, modeset->mode);
> +                               drm_mode_unreference(modeset->mode);
>                         modeset->mode = NULL;
>                 }
>         }
> diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
> index bedf189..55d51ed 100644
> --- a/drivers/gpu/drm/drm_modes.c
> +++ b/drivers/gpu/drm/drm_modes.c
> @@ -82,27 +82,46 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
>                 return NULL;
>         }
>
> +       kref_init(&nmode->refcount);
> +       nmode->dev = dev;
> +
>         return nmode;
>  }
>  EXPORT_SYMBOL(drm_mode_create);
>
>  /**
> - * drm_mode_destroy - remove a mode
> - * @dev: DRM device
> + * drm_mode_reference - incr the mode's refcnt
> + * @mode: display mode
> + *
> + * This functions increments the mode's refcount.
> + */
> +void drm_mode_reference(struct drm_display_mode *mode)
> +{
> +       kref_get(&mode->refcount);
> +}
> +EXPORT_SYMBOL(drm_mode_reference);
> +
> +static void drm_mode_free(struct kref *kref)
> +{
> +       struct drm_display_mode *mode =
> +                       container_of(kref, struct drm_display_mode, refcount);
> +       drm_mode_object_put(mode->dev, &mode->base);
> +       kfree(mode);
> +}
> +
> +/**
> + * drm_mode_unreference - decrement the mode's refcnt
>   * @mode: mode to remove
>   *
> - * Release @mode's unique ID, then free it @mode structure itself using kfree.
> + * Decrement the mode's refcount, freeing it after last ref is dropped.
>   */
> -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
> +void drm_mode_unreference(struct drm_display_mode *mode)
>  {
>         if (!mode)
>                 return;
> -
> -       drm_mode_object_put(dev, &mode->base);
> -
> -       kfree(mode);
> +       kref_put(&mode->refcount, drm_mode_free);
>  }
> -EXPORT_SYMBOL(drm_mode_destroy);
> +EXPORT_SYMBOL(drm_mode_unreference);
>
>  /**
>   * drm_mode_probed_add - add a mode to a connector's probed_mode list
> @@ -957,7 +976,7 @@ void drm_mode_prune_invalid(struct drm_device *dev,
>                                 DRM_DEBUG_KMS("Not using %s mode %d\n",
>                                         mode->name, mode->status);
>                         }
> -                       drm_mode_destroy(dev, mode);
> +                       drm_mode_unreference(mode);
>                 }
>         }
>  }
> @@ -1046,7 +1065,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector,
>                                 else
>                                         mode->type = pmode->type;
>                                 list_del(&pmode->head);
> -                               drm_mode_destroy(connector->dev, pmode);
> +                               drm_mode_unreference(pmode);
>                                 break;
>                         }
>                 }
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
> index ba9b3d5..3f9d44a 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
> @@ -72,7 +72,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
>                 if (display->ops->get_panel)
>                         panel = display->ops->get_panel(display);
>                 else {
> -                       drm_mode_destroy(connector->dev, mode);
> +                       drm_mode_unreference(mode);
>                         return 0;
>                 }
>
> diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> index 0be96fd..00eb0f63 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
> @@ -1905,8 +1905,7 @@ static void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder)
>         struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder);
>
>         if (psb_intel_sdvo->sdvo_lvds_fixed_mode != NULL)
> -               drm_mode_destroy(encoder->dev,
> -                                psb_intel_sdvo->sdvo_lvds_fixed_mode);
> +               drm_mode_unreference(psb_intel_sdvo->sdvo_lvds_fixed_mode);
>
>         i2c_del_adapter(&psb_intel_sdvo->ddc);
>         gma_encoder_destroy(encoder);
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index 38a9857..cbf63e0 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -1205,13 +1205,9 @@ int intel_panel_init(struct intel_panel *panel,
>
>  void intel_panel_fini(struct intel_panel *panel)
>  {
> -       struct intel_connector *intel_connector =
> -               container_of(panel, struct intel_connector, panel);
> -
>         if (panel->fixed_mode)
> -               drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
> +               drm_mode_unreference(panel->fixed_mode);
>
>         if (panel->downclock_mode)
> -               drm_mode_destroy(intel_connector->base.dev,
> -                               panel->downclock_mode);
> +               drm_mode_unreference(panel->downclock_mode);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
> index 9350edd..7049ac7 100644
> --- a/drivers/gpu/drm/i915/intel_sdvo.c
> +++ b/drivers/gpu/drm/i915/intel_sdvo.c
> @@ -2248,8 +2248,7 @@ static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
>         struct intel_sdvo *intel_sdvo = to_sdvo(to_intel_encoder(encoder));
>
>         if (intel_sdvo->sdvo_lvds_fixed_mode != NULL)
> -               drm_mode_destroy(encoder->dev,
> -                                intel_sdvo->sdvo_lvds_fixed_mode);
> +               drm_mode_unreference(intel_sdvo->sdvo_lvds_fixed_mode);
>
>         i2c_del_adapter(&intel_sdvo->ddc);
>         intel_encoder_destroy(encoder);
> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
> index dbdc9ad..876ae75 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_connector.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
> @@ -739,7 +739,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
>         /* destroy the native mode, the attached monitor could have changed.
>          */
>         if (nv_connector->native_mode) {
> -               drm_mode_destroy(dev, nv_connector->native_mode);
> +               drm_mode_unreference(nv_connector->native_mode);
>                 nv_connector->native_mode = NULL;
>         }
>
> diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
> index 36bc5cc..222d42a 100644
> --- a/drivers/gpu/drm/omapdrm/omap_connector.c
> +++ b/drivers/gpu/drm/omapdrm/omap_connector.c
> @@ -224,7 +224,7 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
>                 new_mode->vrefresh = 0;
>                 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
>                         ret = MODE_OK;
> -               drm_mode_destroy(dev, new_mode);
> +               drm_mode_unreference(new_mode);
>         }
>
>         DBG("connector: mode %s: "
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> index d2bc2b0..d726a81 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
> @@ -1964,13 +1964,13 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
>                                                mode->vdisplay)) {
>                         drm_mode_probed_add(connector, mode);
>                 } else {
> -                       drm_mode_destroy(dev, mode);
> +                       drm_mode_unreference(mode);
>                         mode = NULL;
>                 }
>
>                 if (du->pref_mode) {
>                         list_del_init(&du->pref_mode->head);
> -                       drm_mode_destroy(dev, du->pref_mode);
> +                       drm_mode_unreference(du->pref_mode);
>                 }
>
>                 /* mode might be null here, this is intended */
> diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
> index 91d0582..ef552c9 100644
> --- a/include/drm/drm_modes.h
> +++ b/include/drm/drm_modes.h
> @@ -97,6 +97,8 @@ struct drm_display_mode {
>         /* Header */
>         struct list_head head;
>         struct drm_mode_object base;
> +       struct kref refcount;
> +       struct drm_device *dev;
>
>         char name[DRM_DISPLAY_MODE_LEN];
>
> @@ -178,7 +180,8 @@ struct drm_connector;
>  struct drm_cmdline_mode;
>
>  struct drm_display_mode *drm_mode_create(struct drm_device *dev);
> -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
> +void drm_mode_reference(struct drm_display_mode *mode);
> +void drm_mode_unreference(struct drm_display_mode *mode);
>  void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
>  void drm_mode_debug_printmodeline(const struct drm_display_mode *mode);
>
> --
> 1.9.3
>
Rob Clark July 27, 2014, 4:20 p.m. UTC | #2
On Sun, Jul 27, 2014 at 11:17 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sat, Jul 26, 2014 at 12:51 AM, Rob Clark <robdclark@gmail.com> wrote:
>> We're going to need this for atomic.
>>
>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>
> I disagree. Iiui correctly Rob's concern is that the additional stuff
> to keep track of mode lists (list_head and the idr stuff) could
> confuse driver writers into doing stupid stuff when they embed
> drm_display_mode into some other stuff. Imo the right fix would be to
> just remove them (but that's fairly invasive to the mode list code).

bleh, all the deep copies seem ugly either way, I still think
refcnt'ing and shallow copies is the better idea

> Now wrt atomic we only need refcounting because currently
> drm_atomic_state is refcounted. And we don't need that afaics (and I'm
> working on the draft code to show how). So without a clear need for
> refcounting I really prefer we don't add this complexity - doing
> refcounting for fbs wasn't fun at all ;-)

Well, that was somewhat different, because there were some side-effects of rmfb

BR,
-R

> -Daniel
>> ---
>>  drivers/gpu/drm/drm_crtc.c                    |  4 +--
>>  drivers/gpu/drm/drm_crtc_helper.c             |  2 +-
>>  drivers/gpu/drm/drm_edid.c                    |  6 ++--
>>  drivers/gpu/drm/drm_fb_helper.c               |  6 ++--
>>  drivers/gpu/drm/drm_modes.c                   | 41 ++++++++++++++++++++-------
>>  drivers/gpu/drm/exynos/exynos_drm_connector.c |  2 +-
>>  drivers/gpu/drm/gma500/psb_intel_sdvo.c       |  3 +-
>>  drivers/gpu/drm/i915/intel_panel.c            |  8 ++----
>>  drivers/gpu/drm/i915/intel_sdvo.c             |  3 +-
>>  drivers/gpu/drm/nouveau/nouveau_connector.c   |  2 +-
>>  drivers/gpu/drm/omapdrm/omap_connector.c      |  2 +-
>>  drivers/gpu/drm/vmwgfx/vmwgfx_kms.c           |  4 +--
>>  include/drm/drm_modes.h                       |  5 +++-
>>  13 files changed, 52 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index 1ccf5cb..7a7fced 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -822,7 +822,7 @@ static void drm_mode_remove(struct drm_connector *connector,
>>                             struct drm_display_mode *mode)
>>  {
>>         list_del(&mode->head);
>> -       drm_mode_destroy(connector->dev, mode);
>> +       drm_mode_unreference(mode);
>>  }
>>
>>  /**
>> @@ -2602,7 +2602,7 @@ out:
>>                 drm_framebuffer_unreference(fb);
>>
>>         kfree(connector_set);
>> -       drm_mode_destroy(dev, mode);
>> +       drm_mode_unreference(mode);
>>         drm_modeset_unlock_all(dev);
>>         return ret;
>>  }
>> diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
>> index 6c65a0a..757de8b 100644
>> --- a/drivers/gpu/drm/drm_crtc_helper.c
>> +++ b/drivers/gpu/drm/drm_crtc_helper.c
>> @@ -384,7 +384,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
>>
>>         /* FIXME: add subpixel order */
>>  done:
>> -       drm_mode_destroy(dev, adjusted_mode);
>> +       drm_mode_unreference(adjusted_mode);
>>         if (!ret) {
>>                 crtc->enabled = saved_enabled;
>>                 crtc->mode = saved_mode;
>> diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
>> index 087d608..cbc021d 100644
>> --- a/drivers/gpu/drm/drm_edid.c
>> +++ b/drivers/gpu/drm/drm_edid.c
>> @@ -1705,7 +1705,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
>>                 if (!mode)
>>                         return NULL;
>>                 if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
>> -                       drm_mode_destroy(dev, mode);
>> +                       drm_mode_unreference(mode);
>>                         mode = drm_gtf_mode_complex(dev, hsize, vsize,
>>                                                     vrefresh_rate, 0, 0,
>>                                                     drm_gtf2_m(edid),
>> @@ -2021,7 +2021,7 @@ drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
>>                 fixup_mode_1366x768(newmode);
>>                 if (!mode_in_range(newmode, edid, timing) ||
>>                     !valid_inferred_mode(connector, newmode)) {
>> -                       drm_mode_destroy(dev, newmode);
>> +                       drm_mode_unreference(newmode);
>>                         continue;
>>                 }
>>
>> @@ -2050,7 +2050,7 @@ drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
>>                 fixup_mode_1366x768(newmode);
>>                 if (!mode_in_range(newmode, edid, timing) ||
>>                     !valid_inferred_mode(connector, newmode)) {
>> -                       drm_mode_destroy(dev, newmode);
>> +                       drm_mode_unreference(newmode);
>>                         continue;
>>                 }
>>
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 3144db9..5c96a6a 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -588,7 +588,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
>>         for (i = 0; i < helper->crtc_count; i++) {
>>                 kfree(helper->crtc_info[i].mode_set.connectors);
>>                 if (helper->crtc_info[i].mode_set.mode)
>> -                       drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
>> +                       drm_mode_unreference(helper->crtc_info[i].mode_set.mode);
>>         }
>>         kfree(helper->crtc_info);
>>  }
>> @@ -1606,7 +1606,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
>>                                       mode->name, fb_crtc->mode_set.crtc->base.id);
>>                         fb_crtc->desired_mode = mode;
>>                         if (modeset->mode)
>> -                               drm_mode_destroy(dev, modeset->mode);
>> +                               drm_mode_unreference(modeset->mode);
>>                         modeset->mode = drm_mode_duplicate(dev,
>>                                                            fb_crtc->desired_mode);
>>                         modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
>> @@ -1621,7 +1621,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
>>                         BUG_ON(modeset->fb);
>>                         BUG_ON(modeset->num_connectors);
>>                         if (modeset->mode)
>> -                               drm_mode_destroy(dev, modeset->mode);
>> +                               drm_mode_unreference(modeset->mode);
>>                         modeset->mode = NULL;
>>                 }
>>         }
>> diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
>> index bedf189..55d51ed 100644
>> --- a/drivers/gpu/drm/drm_modes.c
>> +++ b/drivers/gpu/drm/drm_modes.c
>> @@ -82,27 +82,46 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
>>                 return NULL;
>>         }
>>
>> +       kref_init(&nmode->refcount);
>> +       nmode->dev = dev;
>> +
>>         return nmode;
>>  }
>>  EXPORT_SYMBOL(drm_mode_create);
>>
>>  /**
>> - * drm_mode_destroy - remove a mode
>> - * @dev: DRM device
>> + * drm_mode_reference - incr the mode's refcnt
>> + * @mode: display mode
>> + *
>> + * This functions increments the mode's refcount.
>> + */
>> +void drm_mode_reference(struct drm_display_mode *mode)
>> +{
>> +       kref_get(&mode->refcount);
>> +}
>> +EXPORT_SYMBOL(drm_mode_reference);
>> +
>> +static void drm_mode_free(struct kref *kref)
>> +{
>> +       struct drm_display_mode *mode =
>> +                       container_of(kref, struct drm_display_mode, refcount);
>> +       drm_mode_object_put(mode->dev, &mode->base);
>> +       kfree(mode);
>> +}
>> +
>> +/**
>> + * drm_mode_unreference - decrement the mode's refcnt
>>   * @mode: mode to remove
>>   *
>> - * Release @mode's unique ID, then free it @mode structure itself using kfree.
>> + * Decrement the mode's refcount, freeing it after last ref is dropped.
>>   */
>> -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
>> +void drm_mode_unreference(struct drm_display_mode *mode)
>>  {
>>         if (!mode)
>>                 return;
>> -
>> -       drm_mode_object_put(dev, &mode->base);
>> -
>> -       kfree(mode);
>> +       kref_put(&mode->refcount, drm_mode_free);
>>  }
>> -EXPORT_SYMBOL(drm_mode_destroy);
>> +EXPORT_SYMBOL(drm_mode_unreference);
>>
>>  /**
>>   * drm_mode_probed_add - add a mode to a connector's probed_mode list
>> @@ -957,7 +976,7 @@ void drm_mode_prune_invalid(struct drm_device *dev,
>>                                 DRM_DEBUG_KMS("Not using %s mode %d\n",
>>                                         mode->name, mode->status);
>>                         }
>> -                       drm_mode_destroy(dev, mode);
>> +                       drm_mode_unreference(mode);
>>                 }
>>         }
>>  }
>> @@ -1046,7 +1065,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector,
>>                                 else
>>                                         mode->type = pmode->type;
>>                                 list_del(&pmode->head);
>> -                               drm_mode_destroy(connector->dev, pmode);
>> +                               drm_mode_unreference(pmode);
>>                                 break;
>>                         }
>>                 }
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
>> index ba9b3d5..3f9d44a 100644
>> --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
>> +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
>> @@ -72,7 +72,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
>>                 if (display->ops->get_panel)
>>                         panel = display->ops->get_panel(display);
>>                 else {
>> -                       drm_mode_destroy(connector->dev, mode);
>> +                       drm_mode_unreference(mode);
>>                         return 0;
>>                 }
>>
>> diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
>> index 0be96fd..00eb0f63 100644
>> --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
>> +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
>> @@ -1905,8 +1905,7 @@ static void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder)
>>         struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder);
>>
>>         if (psb_intel_sdvo->sdvo_lvds_fixed_mode != NULL)
>> -               drm_mode_destroy(encoder->dev,
>> -                                psb_intel_sdvo->sdvo_lvds_fixed_mode);
>> +               drm_mode_unreference(psb_intel_sdvo->sdvo_lvds_fixed_mode);
>>
>>         i2c_del_adapter(&psb_intel_sdvo->ddc);
>>         gma_encoder_destroy(encoder);
>> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
>> index 38a9857..cbf63e0 100644
>> --- a/drivers/gpu/drm/i915/intel_panel.c
>> +++ b/drivers/gpu/drm/i915/intel_panel.c
>> @@ -1205,13 +1205,9 @@ int intel_panel_init(struct intel_panel *panel,
>>
>>  void intel_panel_fini(struct intel_panel *panel)
>>  {
>> -       struct intel_connector *intel_connector =
>> -               container_of(panel, struct intel_connector, panel);
>> -
>>         if (panel->fixed_mode)
>> -               drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
>> +               drm_mode_unreference(panel->fixed_mode);
>>
>>         if (panel->downclock_mode)
>> -               drm_mode_destroy(intel_connector->base.dev,
>> -                               panel->downclock_mode);
>> +               drm_mode_unreference(panel->downclock_mode);
>>  }
>> diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
>> index 9350edd..7049ac7 100644
>> --- a/drivers/gpu/drm/i915/intel_sdvo.c
>> +++ b/drivers/gpu/drm/i915/intel_sdvo.c
>> @@ -2248,8 +2248,7 @@ static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
>>         struct intel_sdvo *intel_sdvo = to_sdvo(to_intel_encoder(encoder));
>>
>>         if (intel_sdvo->sdvo_lvds_fixed_mode != NULL)
>> -               drm_mode_destroy(encoder->dev,
>> -                                intel_sdvo->sdvo_lvds_fixed_mode);
>> +               drm_mode_unreference(intel_sdvo->sdvo_lvds_fixed_mode);
>>
>>         i2c_del_adapter(&intel_sdvo->ddc);
>>         intel_encoder_destroy(encoder);
>> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
>> index dbdc9ad..876ae75 100644
>> --- a/drivers/gpu/drm/nouveau/nouveau_connector.c
>> +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
>> @@ -739,7 +739,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
>>         /* destroy the native mode, the attached monitor could have changed.
>>          */
>>         if (nv_connector->native_mode) {
>> -               drm_mode_destroy(dev, nv_connector->native_mode);
>> +               drm_mode_unreference(nv_connector->native_mode);
>>                 nv_connector->native_mode = NULL;
>>         }
>>
>> diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
>> index 36bc5cc..222d42a 100644
>> --- a/drivers/gpu/drm/omapdrm/omap_connector.c
>> +++ b/drivers/gpu/drm/omapdrm/omap_connector.c
>> @@ -224,7 +224,7 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
>>                 new_mode->vrefresh = 0;
>>                 if (mode->vrefresh == drm_mode_vrefresh(new_mode))
>>                         ret = MODE_OK;
>> -               drm_mode_destroy(dev, new_mode);
>> +               drm_mode_unreference(new_mode);
>>         }
>>
>>         DBG("connector: mode %s: "
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> index d2bc2b0..d726a81 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
>> @@ -1964,13 +1964,13 @@ int vmw_du_connector_fill_modes(struct drm_connector *connector,
>>                                                mode->vdisplay)) {
>>                         drm_mode_probed_add(connector, mode);
>>                 } else {
>> -                       drm_mode_destroy(dev, mode);
>> +                       drm_mode_unreference(mode);
>>                         mode = NULL;
>>                 }
>>
>>                 if (du->pref_mode) {
>>                         list_del_init(&du->pref_mode->head);
>> -                       drm_mode_destroy(dev, du->pref_mode);
>> +                       drm_mode_unreference(du->pref_mode);
>>                 }
>>
>>                 /* mode might be null here, this is intended */
>> diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
>> index 91d0582..ef552c9 100644
>> --- a/include/drm/drm_modes.h
>> +++ b/include/drm/drm_modes.h
>> @@ -97,6 +97,8 @@ struct drm_display_mode {
>>         /* Header */
>>         struct list_head head;
>>         struct drm_mode_object base;
>> +       struct kref refcount;
>> +       struct drm_device *dev;
>>
>>         char name[DRM_DISPLAY_MODE_LEN];
>>
>> @@ -178,7 +180,8 @@ struct drm_connector;
>>  struct drm_cmdline_mode;
>>
>>  struct drm_display_mode *drm_mode_create(struct drm_device *dev);
>> -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
>> +void drm_mode_reference(struct drm_display_mode *mode);
>> +void drm_mode_unreference(struct drm_display_mode *mode);
>>  void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
>>  void drm_mode_debug_printmodeline(const struct drm_display_mode *mode);
>>
>> --
>> 1.9.3
>>
>
>
>
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
Daniel Vetter July 27, 2014, 5:38 p.m. UTC | #3
On Sun, Jul 27, 2014 at 6:20 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Sun, Jul 27, 2014 at 11:17 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>> On Sat, Jul 26, 2014 at 12:51 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> We're going to need this for atomic.
>>>
>>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>>
>> I disagree. Iiui correctly Rob's concern is that the additional stuff
>> to keep track of mode lists (list_head and the idr stuff) could
>> confuse driver writers into doing stupid stuff when they embed
>> drm_display_mode into some other stuff. Imo the right fix would be to
>> just remove them (but that's fairly invasive to the mode list code).
>
> bleh, all the deep copies seem ugly either way, I still think
> refcnt'ing and shallow copies is the better idea

Until people start screaming at me I'm not really concerned with cpu
overhead in the modeset code. We need to do piles of uncached register
writes (at least in most drivers) and it's run about 60 times per
second. Imo we can and should optimize programmer time over cpu time
here. So if deep copies allow us to avoid refcounting, them I'm all
for it.

>> Now wrt atomic we only need refcounting because currently
>> drm_atomic_state is refcounted. And we don't need that afaics (and I'm
>> working on the draft code to show how). So without a clear need for
>> refcounting I really prefer we don't add this complexity - doing
>> refcounting for fbs wasn't fun at all ;-)
>
> Well, that was somewhat different, because there were some side-effects of rmfb

That's not the part I've meant since that's just a gross hack - the
rmfb stuff isn't done on the final unref, only on the final unref from
userspace. And the hack was required to avoid undoing all the benefits
of the finer-grained locking. I'm meaning the inherent auditing we
need to do when adding refcounting, and the potential complexity going
forward.
-Daniel
Rob Clark July 27, 2014, 7:30 p.m. UTC | #4
On Sun, Jul 27, 2014 at 1:38 PM, Daniel Vetter <daniel@ffwll.ch> wrote:
> On Sun, Jul 27, 2014 at 6:20 PM, Rob Clark <robdclark@gmail.com> wrote:
>> On Sun, Jul 27, 2014 at 11:17 AM, Daniel Vetter <daniel@ffwll.ch> wrote:
>>> On Sat, Jul 26, 2014 at 12:51 AM, Rob Clark <robdclark@gmail.com> wrote:
>>>> We're going to need this for atomic.
>>>>
>>>> Signed-off-by: Rob Clark <robdclark@gmail.com>
>>>
>>> I disagree. Iiui correctly Rob's concern is that the additional stuff
>>> to keep track of mode lists (list_head and the idr stuff) could
>>> confuse driver writers into doing stupid stuff when they embed
>>> drm_display_mode into some other stuff. Imo the right fix would be to
>>> just remove them (but that's fairly invasive to the mode list code).
>>
>> bleh, all the deep copies seem ugly either way, I still think
>> refcnt'ing and shallow copies is the better idea
>
> Until people start screaming at me I'm not really concerned with cpu
> overhead in the modeset code. We need to do piles of uncached register
> writes (at least in most drivers) and it's run about 60 times per
> second. Imo we can and should optimize programmer time over cpu time
> here. So if deep copies allow us to avoid refcounting, them I'm all
> for it.

oh, I'm sure it would be hard to measure a difference.. just seems
pointlessly stupid not to do refcnt'ing ;-)

>>> Now wrt atomic we only need refcounting because currently
>>> drm_atomic_state is refcounted. And we don't need that afaics (and I'm
>>> working on the draft code to show how). So without a clear need for
>>> refcounting I really prefer we don't add this complexity - doing
>>> refcounting for fbs wasn't fun at all ;-)
>>
>> Well, that was somewhat different, because there were some side-effects of rmfb
>
> That's not the part I've meant since that's just a gross hack - the
> rmfb stuff isn't done on the final unref, only on the final unref from
> userspace. And the hack was required to avoid undoing all the benefits
> of the finer-grained locking. I'm meaning the inherent auditing we
> need to do when adding refcounting, and the potential complexity going
> forward.

we use refcnt'ing in enough other places, it isn't like it is a new
and foreign concept..

if needed, we can back it up w/ some debugfs to simplify checking for leaks..

BR,
-R

> -Daniel
> --
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 1ccf5cb..7a7fced 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -822,7 +822,7 @@  static void drm_mode_remove(struct drm_connector *connector,
 			    struct drm_display_mode *mode)
 {
 	list_del(&mode->head);
-	drm_mode_destroy(connector->dev, mode);
+	drm_mode_unreference(mode);
 }
 
 /**
@@ -2602,7 +2602,7 @@  out:
 		drm_framebuffer_unreference(fb);
 
 	kfree(connector_set);
-	drm_mode_destroy(dev, mode);
+	drm_mode_unreference(mode);
 	drm_modeset_unlock_all(dev);
 	return ret;
 }
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 6c65a0a..757de8b 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -384,7 +384,7 @@  bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 
 	/* FIXME: add subpixel order */
 done:
-	drm_mode_destroy(dev, adjusted_mode);
+	drm_mode_unreference(adjusted_mode);
 	if (!ret) {
 		crtc->enabled = saved_enabled;
 		crtc->mode = saved_mode;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 087d608..cbc021d 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1705,7 +1705,7 @@  drm_mode_std(struct drm_connector *connector, struct edid *edid,
 		if (!mode)
 			return NULL;
 		if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
-			drm_mode_destroy(dev, mode);
+			drm_mode_unreference(mode);
 			mode = drm_gtf_mode_complex(dev, hsize, vsize,
 						    vrefresh_rate, 0, 0,
 						    drm_gtf2_m(edid),
@@ -2021,7 +2021,7 @@  drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid,
 		fixup_mode_1366x768(newmode);
 		if (!mode_in_range(newmode, edid, timing) ||
 		    !valid_inferred_mode(connector, newmode)) {
-			drm_mode_destroy(dev, newmode);
+			drm_mode_unreference(newmode);
 			continue;
 		}
 
@@ -2050,7 +2050,7 @@  drm_cvt_modes_for_range(struct drm_connector *connector, struct edid *edid,
 		fixup_mode_1366x768(newmode);
 		if (!mode_in_range(newmode, edid, timing) ||
 		    !valid_inferred_mode(connector, newmode)) {
-			drm_mode_destroy(dev, newmode);
+			drm_mode_unreference(newmode);
 			continue;
 		}
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3144db9..5c96a6a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -588,7 +588,7 @@  static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	for (i = 0; i < helper->crtc_count; i++) {
 		kfree(helper->crtc_info[i].mode_set.connectors);
 		if (helper->crtc_info[i].mode_set.mode)
-			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
+			drm_mode_unreference(helper->crtc_info[i].mode_set.mode);
 	}
 	kfree(helper->crtc_info);
 }
@@ -1606,7 +1606,7 @@  static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 				      mode->name, fb_crtc->mode_set.crtc->base.id);
 			fb_crtc->desired_mode = mode;
 			if (modeset->mode)
-				drm_mode_destroy(dev, modeset->mode);
+				drm_mode_unreference(modeset->mode);
 			modeset->mode = drm_mode_duplicate(dev,
 							   fb_crtc->desired_mode);
 			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
@@ -1621,7 +1621,7 @@  static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 			BUG_ON(modeset->fb);
 			BUG_ON(modeset->num_connectors);
 			if (modeset->mode)
-				drm_mode_destroy(dev, modeset->mode);
+				drm_mode_unreference(modeset->mode);
 			modeset->mode = NULL;
 		}
 	}
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index bedf189..55d51ed 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -82,27 +82,46 @@  struct drm_display_mode *drm_mode_create(struct drm_device *dev)
 		return NULL;
 	}
 
+	kref_init(&nmode->refcount);
+	nmode->dev = dev;
+
 	return nmode;
 }
 EXPORT_SYMBOL(drm_mode_create);
 
 /**
- * drm_mode_destroy - remove a mode
- * @dev: DRM device
+ * drm_mode_reference - incr the mode's refcnt
+ * @mode: display mode
+ *
+ * This functions increments the mode's refcount.
+ */
+void drm_mode_reference(struct drm_display_mode *mode)
+{
+	kref_get(&mode->refcount);
+}
+EXPORT_SYMBOL(drm_mode_reference);
+
+static void drm_mode_free(struct kref *kref)
+{
+	struct drm_display_mode *mode =
+			container_of(kref, struct drm_display_mode, refcount);
+	drm_mode_object_put(mode->dev, &mode->base);
+	kfree(mode);
+}
+
+/**
+ * drm_mode_unreference - decrement the mode's refcnt
  * @mode: mode to remove
  *
- * Release @mode's unique ID, then free it @mode structure itself using kfree.
+ * Decrement the mode's refcount, freeing it after last ref is dropped.
  */
-void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
+void drm_mode_unreference(struct drm_display_mode *mode)
 {
 	if (!mode)
 		return;
-
-	drm_mode_object_put(dev, &mode->base);
-
-	kfree(mode);
+	kref_put(&mode->refcount, drm_mode_free);
 }
-EXPORT_SYMBOL(drm_mode_destroy);
+EXPORT_SYMBOL(drm_mode_unreference);
 
 /**
  * drm_mode_probed_add - add a mode to a connector's probed_mode list
@@ -957,7 +976,7 @@  void drm_mode_prune_invalid(struct drm_device *dev,
 				DRM_DEBUG_KMS("Not using %s mode %d\n",
 					mode->name, mode->status);
 			}
-			drm_mode_destroy(dev, mode);
+			drm_mode_unreference(mode);
 		}
 	}
 }
@@ -1046,7 +1065,7 @@  void drm_mode_connector_list_update(struct drm_connector *connector,
 				else
 					mode->type = pmode->type;
 				list_del(&pmode->head);
-				drm_mode_destroy(connector->dev, pmode);
+				drm_mode_unreference(pmode);
 				break;
 			}
 		}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index ba9b3d5..3f9d44a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -72,7 +72,7 @@  static int exynos_drm_connector_get_modes(struct drm_connector *connector)
 		if (display->ops->get_panel)
 			panel = display->ops->get_panel(display);
 		else {
-			drm_mode_destroy(connector->dev, mode);
+			drm_mode_unreference(mode);
 			return 0;
 		}
 
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index 0be96fd..00eb0f63 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -1905,8 +1905,7 @@  static void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder)
 	struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder);
 
 	if (psb_intel_sdvo->sdvo_lvds_fixed_mode != NULL)
-		drm_mode_destroy(encoder->dev,
-				 psb_intel_sdvo->sdvo_lvds_fixed_mode);
+		drm_mode_unreference(psb_intel_sdvo->sdvo_lvds_fixed_mode);
 
 	i2c_del_adapter(&psb_intel_sdvo->ddc);
 	gma_encoder_destroy(encoder);
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 38a9857..cbf63e0 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -1205,13 +1205,9 @@  int intel_panel_init(struct intel_panel *panel,
 
 void intel_panel_fini(struct intel_panel *panel)
 {
-	struct intel_connector *intel_connector =
-		container_of(panel, struct intel_connector, panel);
-
 	if (panel->fixed_mode)
-		drm_mode_destroy(intel_connector->base.dev, panel->fixed_mode);
+		drm_mode_unreference(panel->fixed_mode);
 
 	if (panel->downclock_mode)
-		drm_mode_destroy(intel_connector->base.dev,
-				panel->downclock_mode);
+		drm_mode_unreference(panel->downclock_mode);
 }
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 9350edd..7049ac7 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2248,8 +2248,7 @@  static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
 	struct intel_sdvo *intel_sdvo = to_sdvo(to_intel_encoder(encoder));
 
 	if (intel_sdvo->sdvo_lvds_fixed_mode != NULL)
-		drm_mode_destroy(encoder->dev,
-				 intel_sdvo->sdvo_lvds_fixed_mode);
+		drm_mode_unreference(intel_sdvo->sdvo_lvds_fixed_mode);
 
 	i2c_del_adapter(&intel_sdvo->ddc);
 	intel_encoder_destroy(encoder);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index dbdc9ad..876ae75 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -739,7 +739,7 @@  nouveau_connector_get_modes(struct drm_connector *connector)
 	/* destroy the native mode, the attached monitor could have changed.
 	 */
 	if (nv_connector->native_mode) {
-		drm_mode_destroy(dev, nv_connector->native_mode);
+		drm_mode_unreference(nv_connector->native_mode);
 		nv_connector->native_mode = NULL;
 	}
 
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index 36bc5cc..222d42a 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -224,7 +224,7 @@  static int omap_connector_mode_valid(struct drm_connector *connector,
 		new_mode->vrefresh = 0;
 		if (mode->vrefresh == drm_mode_vrefresh(new_mode))
 			ret = MODE_OK;
-		drm_mode_destroy(dev, new_mode);
+		drm_mode_unreference(new_mode);
 	}
 
 	DBG("connector: mode %s: "
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index d2bc2b0..d726a81 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1964,13 +1964,13 @@  int vmw_du_connector_fill_modes(struct drm_connector *connector,
 					       mode->vdisplay)) {
 			drm_mode_probed_add(connector, mode);
 		} else {
-			drm_mode_destroy(dev, mode);
+			drm_mode_unreference(mode);
 			mode = NULL;
 		}
 
 		if (du->pref_mode) {
 			list_del_init(&du->pref_mode->head);
-			drm_mode_destroy(dev, du->pref_mode);
+			drm_mode_unreference(du->pref_mode);
 		}
 
 		/* mode might be null here, this is intended */
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index 91d0582..ef552c9 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -97,6 +97,8 @@  struct drm_display_mode {
 	/* Header */
 	struct list_head head;
 	struct drm_mode_object base;
+	struct kref refcount;
+	struct drm_device *dev;
 
 	char name[DRM_DISPLAY_MODE_LEN];
 
@@ -178,7 +180,8 @@  struct drm_connector;
 struct drm_cmdline_mode;
 
 struct drm_display_mode *drm_mode_create(struct drm_device *dev);
-void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
+void drm_mode_reference(struct drm_display_mode *mode);
+void drm_mode_unreference(struct drm_display_mode *mode);
 void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
 void drm_mode_debug_printmodeline(const struct drm_display_mode *mode);