[PATCH v4 6/9] drm/tegra: Implement page-flipping support
Mario Kleiner
mario.kleiner at tuebingen.mpg.de
Wed Feb 27 17:09:27 PST 2013
On 02/25/2013 08:06 AM, Thierry Reding wrote:
> On Fri, Feb 22, 2013 at 05:13:47PM +0100, Mario Kleiner wrote:
>> On 02/21/2013 03:35 PM, Thierry Reding wrote:
>>> All the necessary support bits like .mode_set_base() and VBLANK are now
>>> available, so page-flipping case easily be implemented on top.
>>>
>>> Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
>>> ---
>>> Changes in v3:
>>> - use drm_send_vblank_event()
>>> - set crtc->fb field
>>>
>>> Changes in v4:
>>> - fix a potential race by checking that a framebuffer base has been
>>> latched when completing a page-flip
>>>
>>> drivers/gpu/drm/tegra/dc.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
>>> drivers/gpu/drm/tegra/dc.h | 2 ++
>>> drivers/gpu/drm/tegra/drm.c | 9 +++++++
>>> drivers/gpu/drm/tegra/drm.h | 5 ++++
>>> 4 files changed, 82 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
>>> index 5f55a25..c5d825f 100644
>>> --- a/drivers/gpu/drm/tegra/dc.c
>>> +++ b/drivers/gpu/drm/tegra/dc.c
>>> @@ -183,7 +183,72 @@ void tegra_dc_disable_vblank(struct tegra_dc *dc)
>>> spin_unlock_irqrestore(&dc->lock, flags);
>>> }
>>> +static void tegra_dc_finish_page_flip(struct tegra_dc *dc)
>>> +{
>>> + struct drm_device *drm = dc->base.dev;
>>> + struct drm_crtc *crtc = &dc->base;
>>> + struct drm_gem_cma_object *gem;
>>> + unsigned long flags, base;
>>> +
>>
>> The checks for properly latched scanout look good to me and should
>> fix the race between software and hardware. But shouldn't you
>> already spin_lock_irqsave() here, before checking dc->event and
>> performing write access on the DC_CMD_STATE_ACCESS registers? And
>> lock around most of the tegra_dc_page_flip() code below, like in
>> your previous iteration of the patch, to prevent a race between
>> pageflip ioctl and the vblank irq handler?
>
> Currently there's nothing else that touches the DC_CMD_STATE_ACCESS
> register, so that part should be safe.
>
ok.
> As to the locking that I removed since the previous patch, I looked at
> it more closely and didn't think it was necessary. Also nothing in my
> tests seemed to point to any race here, though I probably didn't cover
> everything.
>
>>> + if (!dc->event)
>>
>> -> !dc->event may exit prematurely because the irq handler on one
>> core doesn't notice the new setup of dc->event by
>> tegra_dc_page_flip() on a different core? Don't know if that can
>> happen, don't know the memory ordering of arm, but could imagine
>> that this or the compiler reordering instructions could cause
>> trouble without lock protection.
>
> I'm not sure I follow here. If the handler sees a NULL here, why would
> it want to continue? It can safely assume that the page flip wasn't
> setup for this interval, can't it?
>
> If indeed the VBLANK interrupt happens exactly around the assignment to
> dc->event, then we'll just schedule the page-flip for the next VBLANK.
>
I meant the other way round. dc->event is initially NULL. Then pageflip
ioctl is called, close to vblank, the code e.g., executing on core 1. if
(event) branch in tegra_dc_page_flip() executes, assigns dc->event =
event etc. calls tegra_dc_set_base() before onset of vblank, pageflip
completes in vblank, but the irq handler and thereby
tegra_dc_finish_page_flip() running on a different core doesn't notice
dc->event is non-NULL, because the changes haven't propagated to the
different core without some memory write barrier etc., thereby doesn't
run the flip completion in the vblank of actual flip completion. But i
now think my point is probably pointless, because the successive calls
to drm_vblank_get() and tegra_dc_set_base() contain enough locking and
implicit memory barriers that such a stale value there won't happen.
>>> + return;
>>> +
>>> + gem = drm_fb_cma_get_gem_obj(crtc->fb, 0);
>> -> crtc->fb gets updated in tegra_dc_page_flip() after
>> tegra_dc_set_base() without lock -> possibly stale fb here?
>
> Good point. That may indeed require a lock. I'll need to look more
> closely.
>
Yep, i think here my argument from above may hold for crtc->fb, nothing
here to prevent a race afaics.
>>> + /* check if new start address has been latched */
>>> + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
>>> + base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR);
>>> + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
>>> +
>>
>> -> Can other code in the driver write to DC_CMD_STATE_ACCESS,
>> simultaneously to tegra_dc_page_flip writing->reading->writing and
>> cause trouble?
>
> This is the only location where the DC_CMD_STATE_ACCESS register is
> actually used in the driver so we should be safe for now.
>
Ok. I'm just paranoid about forgetting such things on later changes.
>>> +static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>>> + struct drm_pending_vblank_event *event)
>>> +{
>>> + struct tegra_dc *dc = to_tegra_dc(crtc);
>>> + struct drm_device *drm = crtc->dev;
>>> +
>>> + if (dc->event)
>>> + return -EBUSY;
>>> +
>>
>> -> Lock already here?
>>
>>> + if (event) {
>>> + event->pipe = dc->pipe;
>>> + dc->event = event;
>>> + drm_vblank_get(drm, dc->pipe);
>>> + }
>>> +
>>> + tegra_dc_set_base(dc, 0, 0, fb);
>>> + crtc->fb = fb;
>>> +
>>
>> -> Unlock here?
>
> I tried to expand the lock after we discussed this previously but it
> caused the code to deadlock. While in the process of tracking it down I
> decided that the lock wasn't actually required at all.
>
> But I hadn't thought about the stale FB issue, so I'll check again. I
> notice that Dave has already merged this series, so if nobody objects
> I'll fix it up in a follow-up patch.
>
Fine with me.
thanks,
-mario
More information about the dri-devel
mailing list