diff mbox

drm: fix drm_framebuffer cleanup.

Message ID 1352446770-28855-1-git-send-email-inki.dae@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Inki Dae Nov. 9, 2012, 7:39 a.m. UTC
This patch fixes access issue to invalid memory region.

crtc had only one drm_framebuffer object so when framebuffer
cleanup was requested after page flip, it'd try to disable
hardware overlay to current crtc.
But if current crtc points to another drm_framebuffer,
This may induce invalid memory access.

Assume that some application are doing page flip with two
drm_framebuffer objects. At this time, if second drm_framebuffer
is set to current crtc and the cleanup to first drm_framebuffer
is requested once drm release is requested, then first
drm_framebuffer would be cleaned up without disabling
hardware overlay because current crtc points to second
drm_framebuffer. After that, gem buffer to first drm_framebuffer
would be released and this makes dma access invalid memory region.

This patch adds drm_framebuffer to drm_crtc structure as member
and makes drm_framebuffer_cleanup function check if fb->crtc is
same as desired fb. And also when setcrtc and pageflip are
requested, it makes each drm_framebuffer point to current crtc.

Signed-off-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
---
 drivers/gpu/drm/drm_crtc.c |    7 ++++---
 include/drm/drm_crtc.h     |    1 +
 2 files changed, 5 insertions(+), 3 deletions(-)

Comments

Prathyush K Nov. 9, 2012, 8:49 a.m. UTC | #1
On Fri, Nov 9, 2012 at 1:09 PM, Inki Dae <inki.dae@samsung.com> wrote:

> This patch fixes access issue to invalid memory region.
>
> crtc had only one drm_framebuffer object so when framebuffer
> cleanup was requested after page flip, it'd try to disable
> hardware overlay to current crtc.
> But if current crtc points to another drm_framebuffer,
> This may induce invalid memory access.
>
> Assume that some application are doing page flip with two
> drm_framebuffer objects. At this time, if second drm_framebuffer
> is set to current crtc and the cleanup to first drm_framebuffer
> is requested once drm release is requested, then first
> drm_framebuffer would be cleaned up without disabling
> hardware overlay because current crtc points to second
> drm_framebuffer. After that, gem buffer to first drm_framebuffer
> would be released and this makes dma access invalid memory region.
>


I am confused regarding this. If crtc points to second frame buffer, then
the dma is accessing the memory region
of the second framebuffer. Why can't we free the first framebuffer and its
gem buffer?

With this patch, there is no way to free a framebuffer (which has been set
to a crtc), without disabling
the crtc.

Consider this example:
I have three framebuffers (0, 1, 2) and I set them to a crtc A one by one.
So with your patch,
fb[0]->crtc = A
fb[1]->crtc = A
fb[2]->crtc = A

After this, I am using only framebuffer 0 and 1 i.e. 0 and 1 are being page
flipped.
Now, I want to remove framebuffer 2.
fb[2]->crtc = A .. so while removing the framebuffer, we will end up
disabling the crtc
which is not correct.

I think, there should be an additional interface to unset a fb to a crtc.
That way, we can
reset the crtc inside a framebuffer so that it can be freed if not in use.
i.e. fb[2]->crtc = NULL;






> This patch adds drm_framebuffer to drm_crtc structure as member
> and makes drm_framebuffer_cleanup function check if fb->crtc is
> same as desired fb. And also when setcrtc and pageflip are
> requested, it makes each drm_framebuffer point to current crtc.
>
> Signed-off-by: Inki Dae <inki.dae@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/gpu/drm/drm_crtc.c |    7 ++++---
>  include/drm/drm_crtc.h     |    1 +
>  2 files changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index ef1b221..5c04bd4 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -386,7 +386,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>
>         /* remove from any CRTC */
>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> -               if (crtc->fb == fb) {
> +               if (fb->crtc == crtc) {
>                         /* should turn off the crtc */
>                         memset(&set, 0, sizeof(struct drm_mode_set));
>                         set.crtc = crtc;
> @@ -2027,6 +2027,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void
> *data,
>         set.mode = mode;
>         set.connectors = connector_set;
>         set.num_connectors = crtc_req->count_connectors;
> +       fb->crtc = crtc;
>         set.fb = fb;
>         ret = crtc->funcs->set_config(&set);
>
> @@ -3635,8 +3636,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>                         spin_unlock_irqrestore(&dev->event_lock, flags);
>                         kfree(e);
>                 }
> -       }
> -
> +       } else
> +               fb->crtc = crtc;
>  out:
>         mutex_unlock(&dev->mode_config.mutex);
>         return ret;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 3fa18b7..92889be 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -256,6 +256,7 @@ struct drm_framebuffer {
>         struct kref refcount;
>         struct list_head head;
>         struct drm_mode_object base;
> +       struct drm_crtc *crtc;
>         const struct drm_framebuffer_funcs *funcs;
>         unsigned int pitches[4];
>         unsigned int offsets[4];
> --
> 1.7.4.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Inki Dae Nov. 9, 2012, 9:21 a.m. UTC | #2
2012/11/9 Prathyush K <prathyush@chromium.org>

>
> On Fri, Nov 9, 2012 at 1:09 PM, Inki Dae <inki.dae@samsung.com> wrote:
>
>> This patch fixes access issue to invalid memory region.
>>
>> crtc had only one drm_framebuffer object so when framebuffer
>> cleanup was requested after page flip, it'd try to disable
>> hardware overlay to current crtc.
>> But if current crtc points to another drm_framebuffer,
>> This may induce invalid memory access.
>>
>> Assume that some application are doing page flip with two
>> drm_framebuffer objects. At this time, if second drm_framebuffer
>> is set to current crtc and the cleanup to first drm_framebuffer
>> is requested once drm release is requested, then first
>> drm_framebuffer would be cleaned up without disabling
>> hardware overlay because current crtc points to second
>> drm_framebuffer. After that, gem buffer to first drm_framebuffer
>> would be released and this makes dma access invalid memory region.
>>
>
>
> I am confused regarding this. If crtc points to second frame buffer, then
> the dma is accessing the memory region
> of the second framebuffer. Why can't we free the first framebuffer and its
> gem buffer?
>
> With this patch, there is no way to free a framebuffer (which has been set
> to a crtc), without disabling
> the crtc.
>
> Consider this example:
> I have three framebuffers (0, 1, 2) and I set them to a crtc A one by one.
> So with your patch,
> fb[0]->crtc = A
> fb[1]->crtc = A
> fb[2]->crtc = A
>
> After this, I am using only framebuffer 0 and 1 i.e. 0 and 1 are being
> page flipped.
> Now, I want to remove framebuffer 2.
> fb[2]->crtc = A .. so while removing the framebuffer, we will end up
> disabling the crtc
> which is not correct.
>
> I think, there should be an additional interface to unset a fb to a crtc.
> That way, we can
> reset the crtc inside a framebuffer so that it can be freed if not in use.
> i.e. fb[2]->crtc = NULL;
>
>
>
Right, thank you for comments. There is my missing point. how about adding
fb->crtc = NULL like below then?

int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
*file_priv) {
        ....
        fb->crtc = NULL;
        fb->funcs->destroy(fb);
out:
        ...
}

With this, when user requested rmfb to remove fb[2], fb[2]'s crtc becomes
NULL so the fb would be freed without disabling crtc.

Thanks,
Inki Dae



>
>
>
>
>> This patch adds drm_framebuffer to drm_crtc structure as member
>> and makes drm_framebuffer_cleanup function check if fb->crtc is
>> same as desired fb. And also when setcrtc and pageflip are
>> requested, it makes each drm_framebuffer point to current crtc.
>>
>> Signed-off-by: Inki Dae <inki.dae@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  drivers/gpu/drm/drm_crtc.c |    7 ++++---
>>  include/drm/drm_crtc.h     |    1 +
>>  2 files changed, 5 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
>> index ef1b221..5c04bd4 100644
>> --- a/drivers/gpu/drm/drm_crtc.c
>> +++ b/drivers/gpu/drm/drm_crtc.c
>> @@ -386,7 +386,7 @@ void drm_framebuffer_remove(struct drm_framebuffer
>> *fb)
>>
>>         /* remove from any CRTC */
>>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>> -               if (crtc->fb == fb) {
>> +               if (fb->crtc == crtc) {
>>                         /* should turn off the crtc */
>>                         memset(&set, 0, sizeof(struct drm_mode_set));
>>                         set.crtc = crtc;
>> @@ -2027,6 +2027,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void
>> *data,
>>         set.mode = mode;
>>         set.connectors = connector_set;
>>         set.num_connectors = crtc_req->count_connectors;
>> +       fb->crtc = crtc;
>>         set.fb = fb;
>>         ret = crtc->funcs->set_config(&set);
>>
>> @@ -3635,8 +3636,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>>                         spin_unlock_irqrestore(&dev->event_lock, flags);
>>                         kfree(e);
>>                 }
>> -       }
>> -
>> +       } else
>> +               fb->crtc = crtc;
>>  out:
>>         mutex_unlock(&dev->mode_config.mutex);
>>         return ret;
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index 3fa18b7..92889be 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -256,6 +256,7 @@ struct drm_framebuffer {
>>         struct kref refcount;
>>         struct list_head head;
>>         struct drm_mode_object base;
>> +       struct drm_crtc *crtc;
>>         const struct drm_framebuffer_funcs *funcs;
>>         unsigned int pitches[4];
>>         unsigned int offsets[4];
>> --
>> 1.7.4.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
Ville Syrjälä Nov. 9, 2012, 11:13 a.m. UTC | #3
On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> This patch fixes access issue to invalid memory region.
> 
> crtc had only one drm_framebuffer object so when framebuffer
> cleanup was requested after page flip, it'd try to disable
> hardware overlay to current crtc.
> But if current crtc points to another drm_framebuffer,
> This may induce invalid memory access.
> 
> Assume that some application are doing page flip with two
> drm_framebuffer objects. At this time, if second drm_framebuffer
> is set to current crtc and the cleanup to first drm_framebuffer
> is requested once drm release is requested, then first
> drm_framebuffer would be cleaned up without disabling
> hardware overlay because current crtc points to second
> drm_framebuffer. After that, gem buffer to first drm_framebuffer
> would be released and this makes dma access invalid memory region.
> 
> This patch adds drm_framebuffer to drm_crtc structure as member

That is exactly the reverse of what you're doing in the patch.
We already have crtc.fb, and you're adding fb.crtc.

> and makes drm_framebuffer_cleanup function check if fb->crtc is
> same as desired fb. And also when setcrtc and pageflip are
> requested, it makes each drm_framebuffer point to current crtc.

Looks like you're just setting the crtc.fb and fb.crtc pointers to
exactly mirror each other in the page flip ioctl. That can't fix
anything.

First of all multiple crtcs can scan out from the same fb, so a single
fb.crtc pointer is clearly not enough to represent the relationship
between fbs and crtcs.

Also you're not clearing the fb.crtc pointer anywhere, so now
destroying any framebuffer that was once used for scanout, would disable
some crtc.

So it looks like you're making things worse, not better.

Anyway I'll just nack the whole idea. What you need to do is track the
pending page flips, and make sure the old buffer is not freed too early.
Just grab a reference to the old gem object when issuing the page flip,
and unref it when you're sure the flip has occured. Or you could use the
new drm_framebuffer refcount, but personally I don't see much point in
that when you already have the gem object refcount at your disposal.
Inki Dae Nov. 9, 2012, 12:41 p.m. UTC | #4
2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>

> On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > This patch fixes access issue to invalid memory region.
> >
> > crtc had only one drm_framebuffer object so when framebuffer
> > cleanup was requested after page flip, it'd try to disable
> > hardware overlay to current crtc.
> > But if current crtc points to another drm_framebuffer,
> > This may induce invalid memory access.
> >
> > Assume that some application are doing page flip with two
> > drm_framebuffer objects. At this time, if second drm_framebuffer
> > is set to current crtc and the cleanup to first drm_framebuffer
> > is requested once drm release is requested, then first
> > drm_framebuffer would be cleaned up without disabling
> > hardware overlay because current crtc points to second
> > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > would be released and this makes dma access invalid memory region.
> >
> > This patch adds drm_framebuffer to drm_crtc structure as member
>
> That is exactly the reverse of what you're doing in the patch.
> We already have crtc.fb, and you're adding fb.crtc.
>
> > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > same as desired fb. And also when setcrtc and pageflip are
> > requested, it makes each drm_framebuffer point to current crtc.
>
> Looks like you're just setting the crtc.fb and fb.crtc pointers to
> exactly mirror each other in the page flip ioctl. That can't fix
> anything.
>
>
At least, when drm is released, the crtc to framebuffer that was recently
used for scanout can be disabled correctly.


> First of all multiple crtcs can scan out from the same fb, so a single
> fb.crtc pointer is clearly not enough to represent the relationship
> between fbs and crtcs.
>
> Also you're not clearing the fb.crtc pointer anywhere, so now
> destroying any framebuffer that was once used for scanout, would disable
> some crtc.
>
>
It looks like that you missed my previous comment. Plaese, see the previous
comment. And that was already pointed out by Prathyush. fb.crtc will be
cleared at drm_mode_rmfb(). With this, is there my missing point?  I really
wonder that with this patch, drm framwork could be faced with any problem.


> So it looks like you're making things worse, not better.
>
> Anyway I'll just nack the whole idea. What you need to do is track the
> pending page flips, and make sure the old buffer is not freed too early.
> Just grab a reference to the old gem object when issuing the page flip,
> and unref it when you're sure the flip has occured. Or you could use the
> new drm_framebuffer refcount, but personally I don't see much point in
> that when you already have the gem object refcount at your disposal.
>
>
>

And it seems like that your saying is that each specific drivers
should resolve this issue(access to invalid memory region) tracking the
pending page flip. But I think that this issue may be a missing point drm
framework missed so specific drivers is processing this instead. And with
this patch, couldn't some codes you mentioned be removed from specific
drivers? because it doesn't need to track the pending page flips and
relevant things anymore.

There may be my missing point. I'd welcome to any comments and advices.

Thanks,
Inki Dae
Ville Syrjälä Nov. 9, 2012, 1:30 p.m. UTC | #5
On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > This patch fixes access issue to invalid memory region.
> > >
> > > crtc had only one drm_framebuffer object so when framebuffer
> > > cleanup was requested after page flip, it'd try to disable
> > > hardware overlay to current crtc.
> > > But if current crtc points to another drm_framebuffer,
> > > This may induce invalid memory access.
> > >
> > > Assume that some application are doing page flip with two
> > > drm_framebuffer objects. At this time, if second drm_framebuffer
> > > is set to current crtc and the cleanup to first drm_framebuffer
> > > is requested once drm release is requested, then first
> > > drm_framebuffer would be cleaned up without disabling
> > > hardware overlay because current crtc points to second
> > > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > > would be released and this makes dma access invalid memory region.
> > >
> > > This patch adds drm_framebuffer to drm_crtc structure as member
> >
> > That is exactly the reverse of what you're doing in the patch.
> > We already have crtc.fb, and you're adding fb.crtc.
> >
> > > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > > same as desired fb. And also when setcrtc and pageflip are
> > > requested, it makes each drm_framebuffer point to current crtc.
> >
> > Looks like you're just setting the crtc.fb and fb.crtc pointers to
> > exactly mirror each other in the page flip ioctl. That can't fix
> > anything.
> >
> >
> At least, when drm is released, the crtc to framebuffer that was recently
> used for scanout can be disabled correctly.

Let's take this scenario:

setcrtc(crtc0, fb0)
 -> crtc0.fb = fb0, fb0.crtc = crtc0
page_flip(crtc0, fb1)
 -> crtc0.fb = fb1, fb1.crtc = crtc0
rmfb(fb0)
 -> ?

The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
let's consider this just a bug and ignore it for now. So let's assume
that crtc0 won't be disabled when we call rmfb(fb0).

The real question is, how would your patch help? You can't free fb0
until the page flip has occured, and your patch doesn't track page
flips, so how can it do anything useful?

> > First of all multiple crtcs can scan out from the same fb, so a single
> > fb.crtc pointer is clearly not enough to represent the relationship
> > between fbs and crtcs.
> >
> > Also you're not clearing the fb.crtc pointer anywhere, so now
> > destroying any framebuffer that was once used for scanout, would disable
> > some crtc.
> >
> >
> It looks like that you missed my previous comment. Plaese, see the previous
> comment. And that was already pointed out by Prathyush. fb.crtc will be
> cleared at drm_mode_rmfb(). With this, is there my missing point?  I really
> wonder that with this patch, drm framwork could be faced with any problem.

If you clear the fb.crtc pointer before destroying the fb, then you
never disable any crtcs in response to removing a framebuffer.
That's not what we want when the fb is the current fb for the crtc.

> > So it looks like you're making things worse, not better.
> >
> > Anyway I'll just nack the whole idea. What you need to do is track the
> > pending page flips, and make sure the old buffer is not freed too early.
> > Just grab a reference to the old gem object when issuing the page flip,
> > and unref it when you're sure the flip has occured. Or you could use the
> > new drm_framebuffer refcount, but personally I don't see much point in
> > that when you already have the gem object refcount at your disposal.
> >
> >
> >
> 
> And it seems like that your saying is that each specific drivers
> should resolve this issue(access to invalid memory region) tracking the
> pending page flip. But I think that this issue may be a missing point drm
> framework missed so specific drivers is processing this instead. And with
> this patch, couldn't some codes you mentioned be removed from specific
> drivers? because it doesn't need to track the pending page flips and
> relevant things anymore.
> 
> There may be my missing point. I'd welcome to any comments and advices.

If you don't track the page flips somehow, you can't safely free the
previous buffer. It's that simple. You can of course make some of
that code generic enough to be shared between drivers. That is
actually what the drm_flip mechanism that I wrote for atomic page
flips is; a reasonably generic helper for tracking page flips.
Inki Dae Nov. 9, 2012, 2:04 p.m. UTC | #6
2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>

> On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > This patch fixes access issue to invalid memory region.
> > > >
> > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > cleanup was requested after page flip, it'd try to disable
> > > > hardware overlay to current crtc.
> > > > But if current crtc points to another drm_framebuffer,
> > > > This may induce invalid memory access.
> > > >
> > > > Assume that some application are doing page flip with two
> > > > drm_framebuffer objects. At this time, if second drm_framebuffer
> > > > is set to current crtc and the cleanup to first drm_framebuffer
> > > > is requested once drm release is requested, then first
> > > > drm_framebuffer would be cleaned up without disabling
> > > > hardware overlay because current crtc points to second
> > > > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > > > would be released and this makes dma access invalid memory region.
> > > >
> > > > This patch adds drm_framebuffer to drm_crtc structure as member
> > >
> > > That is exactly the reverse of what you're doing in the patch.
> > > We already have crtc.fb, and you're adding fb.crtc.
> > >
> > > > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > > > same as desired fb. And also when setcrtc and pageflip are
> > > > requested, it makes each drm_framebuffer point to current crtc.
> > >
> > > Looks like you're just setting the crtc.fb and fb.crtc pointers to
> > > exactly mirror each other in the page flip ioctl. That can't fix
> > > anything.
> > >
> > >
> > At least, when drm is released, the crtc to framebuffer that was recently
> > used for scanout can be disabled correctly.
>
> Let's take this scenario:
>
> setcrtc(crtc0, fb0)
>  -> crtc0.fb = fb0, fb0.crtc = crtc0
> page_flip(crtc0, fb1)
>  -> crtc0.fb = fb1, fb1.crtc = crtc0
> rmfb(fb0)
>  -> ?
>
> The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> let's consider this just a bug and ignore it for now. So let's assume
> that crtc0 won't be disabled when we call rmfb(fb0).
>
>
Ok, Please assume that my patch includes the below codes. when we call
rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling crtc.

int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
*file_priv) {
....
fb->crtc = NULL;
fb->funcs->destroy(fb);
out:
...
}




> The real question is, how would your patch help? You can't free fb0
> until the page flip has occured, and your patch doesn't track page
> flips, so how can it do anything useful?
>
> > > First of all multiple crtcs can scan out from the same fb, so a single
> > > fb.crtc pointer is clearly not enough to represent the relationship
> > > between fbs and crtcs.
> > >
> > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > destroying any framebuffer that was once used for scanout, would
> disable
> > > some crtc.
> > >
> > >
> > It looks like that you missed my previous comment. Plaese, see the
> previous
> > comment. And that was already pointed out by Prathyush. fb.crtc will be
> > cleared at drm_mode_rmfb(). With this, is there my missing point?  I
> really
> > wonder that with this patch, drm framwork could be faced with any
> problem.
>
> If you clear the fb.crtc pointer before destroying the fb, then you
> never disable any crtcs in response to removing a framebuffer.
> That's not what we want when the fb is the current fb for the crtc.
>

Right, there is my missing point. How about this then? Couldn't  we check
this fb is last one or not? And if last one, it makes fb->crtc keep as is.


>
> > > So it looks like you're making things worse, not better.
> > >
> > > Anyway I'll just nack the whole idea. What you need to do is track the
> > > pending page flips, and make sure the old buffer is not freed too
> early.
> > > Just grab a reference to the old gem object when issuing the page flip,
> > > and unref it when you're sure the flip has occured. Or you could use
> the
> > > new drm_framebuffer refcount, but personally I don't see much point in
> > > that when you already have the gem object refcount at your disposal.
> > >
> > >
> > >
> >
> > And it seems like that your saying is that each specific drivers
> > should resolve this issue(access to invalid memory region) tracking the
> > pending page flip. But I think that this issue may be a missing point drm
> > framework missed so specific drivers is processing this instead. And with
> > this patch, couldn't some codes you mentioned be removed from specific
> > drivers? because it doesn't need to track the pending page flips and
> > relevant things anymore.
> >
> > There may be my missing point. I'd welcome to any comments and advices.
>
> If you don't track the page flips somehow, you can't safely free the
> previous buffer. It's that simple. You can of course make some of
> that code generic enough to be shared between drivers. That is
> actually what the drm_flip mechanism that I wrote for atomic page
> flips is; a reasonably generic helper for tracking page flips.
>
>
Thanks for comments. Right, now Exynos driver doesn't consider tracking
page flips yet. This is my TODO. Anyway couldn't we improve this patch so
that drm framework considers such thing?
I think with this patch, we could avoid invald memory access issue without
tracking page flips. In this case, pended page flips would be just ignored.

Thanks,
Inki Dae


> --
> Ville Syrjälä
> Intel OTC
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Ville Syrjälä Nov. 9, 2012, 2:29 p.m. UTC | #7
On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > >
> > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > This patch fixes access issue to invalid memory region.
> > > > >
> > > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > > cleanup was requested after page flip, it'd try to disable
> > > > > hardware overlay to current crtc.
> > > > > But if current crtc points to another drm_framebuffer,
> > > > > This may induce invalid memory access.
> > > > >
> > > > > Assume that some application are doing page flip with two
> > > > > drm_framebuffer objects. At this time, if second drm_framebuffer
> > > > > is set to current crtc and the cleanup to first drm_framebuffer
> > > > > is requested once drm release is requested, then first
> > > > > drm_framebuffer would be cleaned up without disabling
> > > > > hardware overlay because current crtc points to second
> > > > > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > > > > would be released and this makes dma access invalid memory region.
> > > > >
> > > > > This patch adds drm_framebuffer to drm_crtc structure as member
> > > >
> > > > That is exactly the reverse of what you're doing in the patch.
> > > > We already have crtc.fb, and you're adding fb.crtc.
> > > >
> > > > > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > > > > same as desired fb. And also when setcrtc and pageflip are
> > > > > requested, it makes each drm_framebuffer point to current crtc.
> > > >
> > > > Looks like you're just setting the crtc.fb and fb.crtc pointers to
> > > > exactly mirror each other in the page flip ioctl. That can't fix
> > > > anything.
> > > >
> > > >
> > > At least, when drm is released, the crtc to framebuffer that was recently
> > > used for scanout can be disabled correctly.
> >
> > Let's take this scenario:
> >
> > setcrtc(crtc0, fb0)
> >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > page_flip(crtc0, fb1)
> >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > rmfb(fb0)
> >  -> ?
> >
> > The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> > let's consider this just a bug and ignore it for now. So let's assume
> > that crtc0 won't be disabled when we call rmfb(fb0).
> >
> >
> Ok, Please assume that my patch includes the below codes. when we call
> rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling crtc.
> 
> int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
> *file_priv) {
> ....
> fb->crtc = NULL;
> fb->funcs->destroy(fb);
> out:
> ...
> }

As stated above, I already assumed that.

> > The real question is, how would your patch help? You can't free fb0
> > until the page flip has occured, and your patch doesn't track page
> > flips, so how can it do anything useful?
> >
> > > > First of all multiple crtcs can scan out from the same fb, so a single
> > > > fb.crtc pointer is clearly not enough to represent the relationship
> > > > between fbs and crtcs.
> > > >
> > > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > > destroying any framebuffer that was once used for scanout, would
> > disable
> > > > some crtc.
> > > >
> > > >
> > > It looks like that you missed my previous comment. Plaese, see the
> > previous
> > > comment. And that was already pointed out by Prathyush. fb.crtc will be
> > > cleared at drm_mode_rmfb(). With this, is there my missing point?  I
> > really
> > > wonder that with this patch, drm framwork could be faced with any
> > problem.
> >
> > If you clear the fb.crtc pointer before destroying the fb, then you
> > never disable any crtcs in response to removing a framebuffer.
> > That's not what we want when the fb is the current fb for the crtc.
> >
> 
> Right, there is my missing point. How about this then? Couldn't  we check
> this fb is last one or not? And if last one, it makes fb->crtc keep as is.

That won't help, It would just make the behaviour of your code
identical to the behaviour of the current code.

> > > > So it looks like you're making things worse, not better.
> > > >
> > > > Anyway I'll just nack the whole idea. What you need to do is track the
> > > > pending page flips, and make sure the old buffer is not freed too
> > early.
> > > > Just grab a reference to the old gem object when issuing the page flip,
> > > > and unref it when you're sure the flip has occured. Or you could use
> > the
> > > > new drm_framebuffer refcount, but personally I don't see much point in
> > > > that when you already have the gem object refcount at your disposal.
> > > >
> > > >
> > > >
> > >
> > > And it seems like that your saying is that each specific drivers
> > > should resolve this issue(access to invalid memory region) tracking the
> > > pending page flip. But I think that this issue may be a missing point drm
> > > framework missed so specific drivers is processing this instead. And with
> > > this patch, couldn't some codes you mentioned be removed from specific
> > > drivers? because it doesn't need to track the pending page flips and
> > > relevant things anymore.
> > >
> > > There may be my missing point. I'd welcome to any comments and advices.
> >
> > If you don't track the page flips somehow, you can't safely free the
> > previous buffer. It's that simple. You can of course make some of
> > that code generic enough to be shared between drivers. That is
> > actually what the drm_flip mechanism that I wrote for atomic page
> > flips is; a reasonably generic helper for tracking page flips.
> >
> >
> Thanks for comments. Right, now Exynos driver doesn't consider tracking
> page flips yet. This is my TODO. Anyway couldn't we improve this patch so
> that drm framework considers such thing?

No, because it depends on hardware specific information. The drm core
code doesn't have a crystall ball, so it can't just magically know when
a page flip completes.

> I think with this patch, we could avoid invald memory access issue without
> tracking page flips. In this case, pended page flips would be just ignored.

I still don't understand how the patch is supposed to help. If you make
the proposed change to rmfb() so that it doesn't disable the crtc when
you remove a non-current fb, then this would happen:

setcrtc(crtc0, fb0)
 -> crtc0.fb = fb0, fb0.crtc = crtc0
pageflip(crtc0, fb1);
 -> crtc0.fb = fb1, fb1.crtc = crtc0
rmfb(fb0);
 -> fb0.crtc = NULL;
    destroy(fb0);

That is exactly the same behaviour we have today, so you still get
the invalid memory access to fb0 if the page flip hasn't occured yet.
And that means the fb.crtc pointer is entirely pointless.
Rob Clark Nov. 9, 2012, 2:40 p.m. UTC | #8
On Fri, Nov 9, 2012 at 1:39 AM, Inki Dae <inki.dae@samsung.com> wrote:
> This patch fixes access issue to invalid memory region.
>
> crtc had only one drm_framebuffer object so when framebuffer
> cleanup was requested after page flip, it'd try to disable
> hardware overlay to current crtc.
> But if current crtc points to another drm_framebuffer,
> This may induce invalid memory access.

btw, this should instead be done by holding a ref to the GEM
object(s)..  or these days you can increment the reference count on
the fb and let the fb hold ref's to the GEM object(s) (which makes it
a bit easier to deal with multi-planar formats)

BR,
-R

> Assume that some application are doing page flip with two
> drm_framebuffer objects. At this time, if second drm_framebuffer
> is set to current crtc and the cleanup to first drm_framebuffer
> is requested once drm release is requested, then first
> drm_framebuffer would be cleaned up without disabling
> hardware overlay because current crtc points to second
> drm_framebuffer. After that, gem buffer to first drm_framebuffer
> would be released and this makes dma access invalid memory region.
>
> This patch adds drm_framebuffer to drm_crtc structure as member
> and makes drm_framebuffer_cleanup function check if fb->crtc is
> same as desired fb. And also when setcrtc and pageflip are
> requested, it makes each drm_framebuffer point to current crtc.
>
> Signed-off-by: Inki Dae <inki.dae@samsung.com>
> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> ---
>  drivers/gpu/drm/drm_crtc.c |    7 ++++---
>  include/drm/drm_crtc.h     |    1 +
>  2 files changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index ef1b221..5c04bd4 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -386,7 +386,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>
>         /* remove from any CRTC */
>         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> -               if (crtc->fb == fb) {
> +               if (fb->crtc == crtc) {
>                         /* should turn off the crtc */
>                         memset(&set, 0, sizeof(struct drm_mode_set));
>                         set.crtc = crtc;
> @@ -2027,6 +2027,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
>         set.mode = mode;
>         set.connectors = connector_set;
>         set.num_connectors = crtc_req->count_connectors;
> +       fb->crtc = crtc;
>         set.fb = fb;
>         ret = crtc->funcs->set_config(&set);
>
> @@ -3635,8 +3636,8 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>                         spin_unlock_irqrestore(&dev->event_lock, flags);
>                         kfree(e);
>                 }
> -       }
> -
> +       } else
> +               fb->crtc = crtc;
>  out:
>         mutex_unlock(&dev->mode_config.mutex);
>         return ret;
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 3fa18b7..92889be 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -256,6 +256,7 @@ struct drm_framebuffer {
>         struct kref refcount;
>         struct list_head head;
>         struct drm_mode_object base;
> +       struct drm_crtc *crtc;
>         const struct drm_framebuffer_funcs *funcs;
>         unsigned int pitches[4];
>         unsigned int offsets[4];
> --
> 1.7.4.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Inki Dae Nov. 9, 2012, 4:50 p.m. UTC | #9
2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>

> On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > >
> > > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > > This patch fixes access issue to invalid memory region.
> > > > > >
> > > > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > > > cleanup was requested after page flip, it'd try to disable
> > > > > > hardware overlay to current crtc.
> > > > > > But if current crtc points to another drm_framebuffer,
> > > > > > This may induce invalid memory access.
> > > > > >
> > > > > > Assume that some application are doing page flip with two
> > > > > > drm_framebuffer objects. At this time, if second drm_framebuffer
> > > > > > is set to current crtc and the cleanup to first drm_framebuffer
> > > > > > is requested once drm release is requested, then first
> > > > > > drm_framebuffer would be cleaned up without disabling
> > > > > > hardware overlay because current crtc points to second
> > > > > > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > > > > > would be released and this makes dma access invalid memory
> region.
> > > > > >
> > > > > > This patch adds drm_framebuffer to drm_crtc structure as member
> > > > >
> > > > > That is exactly the reverse of what you're doing in the patch.
> > > > > We already have crtc.fb, and you're adding fb.crtc.
> > > > >
> > > > > > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > > > > > same as desired fb. And also when setcrtc and pageflip are
> > > > > > requested, it makes each drm_framebuffer point to current crtc.
> > > > >
> > > > > Looks like you're just setting the crtc.fb and fb.crtc pointers to
> > > > > exactly mirror each other in the page flip ioctl. That can't fix
> > > > > anything.
> > > > >
> > > > >
> > > > At least, when drm is released, the crtc to framebuffer that was
> recently
> > > > used for scanout can be disabled correctly.
> > >
> > > Let's take this scenario:
> > >
> > > setcrtc(crtc0, fb0)
> > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > page_flip(crtc0, fb1)
> > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > rmfb(fb0)
> > >  -> ?
> > >
> > > The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> > > let's consider this just a bug and ignore it for now. So let's assume
> > > that crtc0 won't be disabled when we call rmfb(fb0).
> > >
> > >
> > Ok, Please assume that my patch includes the below codes. when we call
> > rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling
> crtc.
> >
> > int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
> > *file_priv) {
> > ....
> > fb->crtc = NULL;
> > fb->funcs->destroy(fb);
> > out:
> > ...
> > }
>
> As stated above, I already assumed that.
>
> > > The real question is, how would your patch help? You can't free fb0
> > > until the page flip has occured, and your patch doesn't track page
> > > flips, so how can it do anything useful?
> > >
> > > > > First of all multiple crtcs can scan out from the same fb, so a
> single
> > > > > fb.crtc pointer is clearly not enough to represent the relationship
> > > > > between fbs and crtcs.
> > > > >
> > > > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > > > destroying any framebuffer that was once used for scanout, would
> > > disable
> > > > > some crtc.
> > > > >
> > > > >
> > > > It looks like that you missed my previous comment. Plaese, see the
> > > previous
> > > > comment. And that was already pointed out by Prathyush. fb.crtc will
> be
> > > > cleared at drm_mode_rmfb(). With this, is there my missing point?  I
> > > really
> > > > wonder that with this patch, drm framwork could be faced with any
> > > problem.
> > >
> > > If you clear the fb.crtc pointer before destroying the fb, then you
> > > never disable any crtcs in response to removing a framebuffer.
> > > That's not what we want when the fb is the current fb for the crtc.
> > >
> >
> > Right, there is my missing point. How about this then? Couldn't  we check
> > this fb is last one or not? And if last one, it makes fb->crtc keep as
> is.
>
> That won't help, It would just make the behaviour of your code
> identical to the behaviour of the current code.
>
> > > > > So it looks like you're making things worse, not better.
> > > >
> > > > Anyway I'll just nack the whole idea. What you need to do is track
the
> > > > pending page flips, and make sure the old buffer is not freed too
> > early.
> > > > Just grab a reference to the old gem object when issuing the page
flip,
> > > > and unref it when you're sure the flip has occured. Or you could use
> > the
> > > > new drm_framebuffer refcount, but personally I don't see much point
in
> > > > that when you already have the gem object refcount at your disposal.
> > > >
> > > >
> > > >
> > >
> > > And it seems like that your saying is that each specific drivers
> > > should resolve this issue(access to invalid memory region) tracking
the
> > > pending page flip. But I think that this issue may be a missing point
drm
> > > framework missed so specific drivers is processing this instead. And
with
> > > this patch, couldn't some codes you mentioned be removed from specific
> > > drivers? because it doesn't need to track the pending page flips and
> > > relevant things anymore.
> > >
> > > There may be my missing point. I'd welcome to any comments and
advices.
> >
> > If you don't track the page flips somehow, you can't safely free the
> > previous buffer. It's that simple. You can of course make some of
> > that code generic enough to be shared between drivers. That is
> > actually what the drm_flip mechanism that I wrote for atomic page
> > flips is; a reasonably generic helper for tracking page flips.
> >
> >
> Thanks for comments. Right, now Exynos driver doesn't consider tracking
> page flips yet. This is my TODO. Anyway couldn't we improve this patch so
> that drm framework considers such thing?

> No, because it depends on hardware specific information. The drm core
> code doesn't have a crystall ball, so it can't just magically know when
> a page flip completes.
>
> > I think with this patch, we could avoid invald memory access issue
> without
> > tracking page flips. In this case, pended page flips would be just
> ignored.
>
> I still don't understand how the patch is supposed to help. If you make
> the proposed change to rmfb() so that it doesn't disable the crtc when
> you remove a non-current fb, then this would happen:
>
> setcrtc(crtc0, fb0)
>  -> crtc0.fb = fb0, fb0.crtc = crtc0
> pageflip(crtc0, fb1);
>  -> crtc0.fb = fb1, fb1.crtc = crtc0
> rmfb(fb0);
>  -> fb0.crtc = NULL;
>     destroy(fb0);
>
> That is exactly the same behaviour we have today, so you still get
> the invalid memory access to fb0 if the page flip hasn't occured yet.
> And that means the fb.crtc pointer is entirely pointless.
>
>
I don't think so. let's see it again. below is my idea AND the reason I
posted this patch.
Original codes,
gem alloc(gem0);
-> gem0 refcount = 1
gem0 mmap
-> gem0 refcount = 2
gem alloc(gem1);
-> gem1 refcount =1
gem1 mmap
-> gem1 refcount =2
addfb(fb0, gem0);
-> gem0 refcount=3
addfb(fb1,gem1);
-> gem1 refcount = 3
setcrtc(fb0, crtc0)
-> crtc0.fb = fb0
pageflip(crtc0, fb1);
-> crtc0.fb = fb1.
and pageflip is repeated

close(gem0)
-> gem0 refcount = 2
close(gem1)
->gem1 refcount = 2
munmap(gem0)
->gem0 refcount = 1
munmap(gem1)
->gem1 refcount = 1

close(drm)
1. fb release
-> check if crtc->fb is same as fb0 but current crtc is pointing to fb1
2. so free fb0 without disabling current crtc.
-> gem0 refcount = 0 so released. At this time, dma access invalid memory
region unfortunately* *if the dma is accessing gem0.



With my patch,
...
setcrtc(fb0, crtc0)
-> crtc0.fb = fb0, fb0.crtc = crtc0
pageflip(crtc0, fb1);
-> crtc0.fb = fb1, fb1.crtc = crtc0.
and pageflip is repeated

close(gem0)
-> gem0 refcount = 2
close(gem1)
->gem1 refcount = 2
munmap(gem0)
->gem0 refcount = 1
munmap(gem1)
->gem1 refcount = 1
close(drm)
1. fb release
-> fb0->crtc is same as crtc so disable crtc (dma stop)
2. and free fb0.
-> gem0 refcount = 0 so released. We can avoid invalid memory access
because the dma was stopped.
In addition, one thing you pointed should be considered like below,
1. When rmfb is called, if fb is last one then it sets fb->crtc to NULL.
- the fb is cleaned up with disabling crtc.

That's all and the issue I faced with.

I'd happy to give me any comments, opinions.

Thanks,
Inki Dae



> --
> Ville Syrjälä
> Intel OTC
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Inki Dae Nov. 9, 2012, 4:56 p.m. UTC | #10
2012/11/9 Rob Clark <robdclark@gmail.com>

> On Fri, Nov 9, 2012 at 1:39 AM, Inki Dae <inki.dae@samsung.com> wrote:
> > This patch fixes access issue to invalid memory region.
> >
> > crtc had only one drm_framebuffer object so when framebuffer
> > cleanup was requested after page flip, it'd try to disable
> > hardware overlay to current crtc.
> > But if current crtc points to another drm_framebuffer,
> > This may induce invalid memory access.
>
> btw, this should instead be done by holding a ref to the GEM
> object(s)..  or these days you can increment the reference count on
> the fb and let the fb hold ref's to the GEM object(s) (which makes it
> a bit easier to deal with multi-planar formats)
>
>
Rob, let's discuss that again after you read my latest comment. Please, see
my latest comment.

Thanks,
Inki Dae


> BR,
> -R
>
> > Assume that some application are doing page flip with two
> > drm_framebuffer objects. At this time, if second drm_framebuffer
> > is set to current crtc and the cleanup to first drm_framebuffer
> > is requested once drm release is requested, then first
> > drm_framebuffer would be cleaned up without disabling
> > hardware overlay because current crtc points to second
> > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > would be released and this makes dma access invalid memory region.
> >
> > This patch adds drm_framebuffer to drm_crtc structure as member
> > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > same as desired fb. And also when setcrtc and pageflip are
> > requested, it makes each drm_framebuffer point to current crtc.
> >
> > Signed-off-by: Inki Dae <inki.dae@samsung.com>
> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
> > ---
> >  drivers/gpu/drm/drm_crtc.c |    7 ++++---
> >  include/drm/drm_crtc.h     |    1 +
> >  2 files changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> > index ef1b221..5c04bd4 100644
> > --- a/drivers/gpu/drm/drm_crtc.c
> > +++ b/drivers/gpu/drm/drm_crtc.c
> > @@ -386,7 +386,7 @@ void drm_framebuffer_remove(struct drm_framebuffer
> *fb)
> >
> >         /* remove from any CRTC */
> >         list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> > -               if (crtc->fb == fb) {
> > +               if (fb->crtc == crtc) {
> >                         /* should turn off the crtc */
> >                         memset(&set, 0, sizeof(struct drm_mode_set));
> >                         set.crtc = crtc;
> > @@ -2027,6 +2027,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void
> *data,
> >         set.mode = mode;
> >         set.connectors = connector_set;
> >         set.num_connectors = crtc_req->count_connectors;
> > +       fb->crtc = crtc;
> >         set.fb = fb;
> >         ret = crtc->funcs->set_config(&set);
> >
> > @@ -3635,8 +3636,8 @@ int drm_mode_page_flip_ioctl(struct drm_device
> *dev,
> >                         spin_unlock_irqrestore(&dev->event_lock, flags);
> >                         kfree(e);
> >                 }
> > -       }
> > -
> > +       } else
> > +               fb->crtc = crtc;
> >  out:
> >         mutex_unlock(&dev->mode_config.mutex);
> >         return ret;
> > diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> > index 3fa18b7..92889be 100644
> > --- a/include/drm/drm_crtc.h
> > +++ b/include/drm/drm_crtc.h
> > @@ -256,6 +256,7 @@ struct drm_framebuffer {
> >         struct kref refcount;
> >         struct list_head head;
> >         struct drm_mode_object base;
> > +       struct drm_crtc *crtc;
> >         const struct drm_framebuffer_funcs *funcs;
> >         unsigned int pitches[4];
> >         unsigned int offsets[4];
> > --
> > 1.7.4.1
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Rob Clark Nov. 9, 2012, 4:57 p.m. UTC | #11
On Fri, Nov 9, 2012 at 10:50 AM, Inki Dae <inki.dae@samsung.com> wrote:
> I don't think so. let's see it again. below is my idea AND the reason I
> posted this patch.
> Original codes,
> gem alloc(gem0);
> -> gem0 refcount = 1
> gem0 mmap
> -> gem0 refcount = 2
> gem alloc(gem1);
> -> gem1 refcount =1
> gem1 mmap
> -> gem1 refcount =2
> addfb(fb0, gem0);
> -> gem0 refcount=3
> addfb(fb1,gem1);
> -> gem1 refcount = 3
> setcrtc(fb0, crtc0)
> -> crtc0.fb = fb0
> pageflip(crtc0, fb1);
> -> crtc0.fb = fb1.
> and pageflip is repeated
>
> close(gem0)
> -> gem0 refcount = 2
> close(gem1)
> ->gem1 refcount = 2
> munmap(gem0)
> ->gem0 refcount = 1
> munmap(gem1)
> ->gem1 refcount = 1
>
> close(drm)
> 1. fb release
> -> check if crtc->fb is same as fb0 but current crtc is pointing to fb1
> 2. so free fb0 without disabling current crtc.
> -> gem0 refcount = 0 so released. At this time, dma access invalid memory
> region unfortunately if the dma is accessing gem0.
>

I think you should either hold an extra ref to the fb until dma stops,
or block when the fb is destroyed until dma stops.

See the 'drm: refcnt drm_framebuffer' patch.  This lets me solve a
similar issue in omapdrm.  (Well, you could solve the same issue by
holding a ref to the GEM objects somewhere else until the scanout
stops, but it is easier for the crtc just to hold a ref to the fb.)

BR,
-R

>
>
> With my patch,
> ...
> setcrtc(fb0, crtc0)
> -> crtc0.fb = fb0, fb0.crtc = crtc0
> pageflip(crtc0, fb1);
> -> crtc0.fb = fb1, fb1.crtc = crtc0.
> and pageflip is repeated
>
> close(gem0)
> -> gem0 refcount = 2
> close(gem1)
> ->gem1 refcount = 2
> munmap(gem0)
> ->gem0 refcount = 1
> munmap(gem1)
> ->gem1 refcount = 1
> close(drm)
> 1. fb release
> -> fb0->crtc is same as crtc so disable crtc (dma stop)
> 2. and free fb0.
> -> gem0 refcount = 0 so released. We can avoid invalid memory access because
> the dma was stopped.
> In addition, one thing you pointed should be considered like below,
> 1. When rmfb is called, if fb is last one then it sets fb->crtc to NULL.
> - the fb is cleaned up with disabling crtc.
>
Rob Clark Nov. 9, 2012, 5 p.m. UTC | #12
On Fri, Nov 9, 2012 at 10:56 AM, Inki Dae <inki.dae@samsung.com> wrote:
>
>
> 2012/11/9 Rob Clark <robdclark@gmail.com>
>>
>> On Fri, Nov 9, 2012 at 1:39 AM, Inki Dae <inki.dae@samsung.com> wrote:
>> > This patch fixes access issue to invalid memory region.
>> >
>> > crtc had only one drm_framebuffer object so when framebuffer
>> > cleanup was requested after page flip, it'd try to disable
>> > hardware overlay to current crtc.
>> > But if current crtc points to another drm_framebuffer,
>> > This may induce invalid memory access.
>>
>> btw, this should instead be done by holding a ref to the GEM
>> object(s)..  or these days you can increment the reference count on
>> the fb and let the fb hold ref's to the GEM object(s) (which makes it
>> a bit easier to deal with multi-planar formats)
>>
>
> Rob, let's discuss that again after you read my latest comment. Please, see
> my latest comment.

oh, I think our emails crossed paths.  But I think the crtc (or plane)
holding a ref to the fb until scanout stops should solve the issue for
you.

BR,
-R

> Thanks,
> Inki Dae
>
Rob Clark Nov. 9, 2012, 5:10 p.m. UTC | #13
On Fri, Nov 9, 2012 at 1:39 AM, Inki Dae <inki.dae@samsung.com> wrote:
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 3fa18b7..92889be 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -256,6 +256,7 @@ struct drm_framebuffer {
>         struct kref refcount;
>         struct list_head head;
>         struct drm_mode_object base;
> +       struct drm_crtc *crtc;

note that one fb can be attached to multiple crtc's (and/or planes)..
so this will never work.

Anyways, I think the right answer is somehow reference-counting.  You
should *somewhere* be holding a ref to bo's until there is no more dma
access, either directly or indirectly by holding a ref to the fb
(which holds a ref to the bo(s))

BR,
-R

>         const struct drm_framebuffer_funcs *funcs;
>         unsigned int pitches[4];
>         unsigned int offsets[4];
> --
> 1.7.4.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Inki Dae Nov. 9, 2012, 5:39 p.m. UTC | #14
2012. 11. 10. ?? 2:10 Rob Clark <robdclark@gmail.com> ??:

> On Fri, Nov 9, 2012 at 1:39 AM, Inki Dae <inki.dae@samsung.com> wrote:
>> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
>> index 3fa18b7..92889be 100644
>> --- a/include/drm/drm_crtc.h
>> +++ b/include/drm/drm_crtc.h
>> @@ -256,6 +256,7 @@ struct drm_framebuffer {
>>        struct kref refcount;
>>        struct list_head head;
>>        struct drm_mode_object base;
>> +       struct drm_crtc *crtc;
> 
> note that one fb can be attached to multiple crtc's (and/or planes)..
> so this will never work.
> 

I know that. drm_framebuffer'crtc means that crtc'dma is accessing this fb. So when pageflip and setcrtc are called, this crtc will be updated again.

> Anyways, I think the right answer is somehow reference-counting.  You
> should *somewhere* be holding a ref to bo's until there is no more dma
> access, either directly or indirectly by holding a ref to the fb
> (which holds a ref to the bo(s))
> 

Understood. Yes, we could resolve this issue using reference-counting also. I'd just like to make this issue to be solved more generically. In fact, I couldn't understand why my patch has some problem yet. :(

Thanks for comments,
Inki Dae

> BR,
> -R
> 
>>        const struct drm_framebuffer_funcs *funcs;
>>        unsigned int pitches[4];
>>        unsigned int offsets[4];
>> --
>> 1.7.4.1
>> 
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Ville Syrjälä Nov. 9, 2012, 6:48 p.m. UTC | #15
On Sat, Nov 10, 2012 at 01:50:53AM +0900, Inki Dae wrote:
> 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> > On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > >
> > > > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > >
> > > > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > > > This patch fixes access issue to invalid memory region.
> > > > > > >
> > > > > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > > > > cleanup was requested after page flip, it'd try to disable
> > > > > > > hardware overlay to current crtc.
> > > > > > > But if current crtc points to another drm_framebuffer,
> > > > > > > This may induce invalid memory access.
> > > > > > >
> > > > > > > Assume that some application are doing page flip with two
> > > > > > > drm_framebuffer objects. At this time, if second drm_framebuffer
> > > > > > > is set to current crtc and the cleanup to first drm_framebuffer
> > > > > > > is requested once drm release is requested, then first
> > > > > > > drm_framebuffer would be cleaned up without disabling
> > > > > > > hardware overlay because current crtc points to second
> > > > > > > drm_framebuffer. After that, gem buffer to first drm_framebuffer
> > > > > > > would be released and this makes dma access invalid memory
> > region.
> > > > > > >
> > > > > > > This patch adds drm_framebuffer to drm_crtc structure as member
> > > > > >
> > > > > > That is exactly the reverse of what you're doing in the patch.
> > > > > > We already have crtc.fb, and you're adding fb.crtc.
> > > > > >
> > > > > > > and makes drm_framebuffer_cleanup function check if fb->crtc is
> > > > > > > same as desired fb. And also when setcrtc and pageflip are
> > > > > > > requested, it makes each drm_framebuffer point to current crtc.
> > > > > >
> > > > > > Looks like you're just setting the crtc.fb and fb.crtc pointers to
> > > > > > exactly mirror each other in the page flip ioctl. That can't fix
> > > > > > anything.
> > > > > >
> > > > > >
> > > > > At least, when drm is released, the crtc to framebuffer that was
> > recently
> > > > > used for scanout can be disabled correctly.
> > > >
> > > > Let's take this scenario:
> > > >
> > > > setcrtc(crtc0, fb0)
> > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > page_flip(crtc0, fb1)
> > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > rmfb(fb0)
> > > >  -> ?
> > > >
> > > > The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> > > > let's consider this just a bug and ignore it for now. So let's assume
> > > > that crtc0 won't be disabled when we call rmfb(fb0).
> > > >
> > > >
> > > Ok, Please assume that my patch includes the below codes. when we call
> > > rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling
> > crtc.
> > >
> > > int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
> > > *file_priv) {
> > > ....
> > > fb->crtc = NULL;
> > > fb->funcs->destroy(fb);
> > > out:
> > > ...
> > > }
> >
> > As stated above, I already assumed that.
> >
> > > > The real question is, how would your patch help? You can't free fb0
> > > > until the page flip has occured, and your patch doesn't track page
> > > > flips, so how can it do anything useful?
> > > >
> > > > > > First of all multiple crtcs can scan out from the same fb, so a
> > single
> > > > > > fb.crtc pointer is clearly not enough to represent the relationship
> > > > > > between fbs and crtcs.
> > > > > >
> > > > > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > > > > destroying any framebuffer that was once used for scanout, would
> > > > disable
> > > > > > some crtc.
> > > > > >
> > > > > >
> > > > > It looks like that you missed my previous comment. Plaese, see the
> > > > previous
> > > > > comment. And that was already pointed out by Prathyush. fb.crtc will
> > be
> > > > > cleared at drm_mode_rmfb(). With this, is there my missing point?  I
> > > > really
> > > > > wonder that with this patch, drm framwork could be faced with any
> > > > problem.
> > > >
> > > > If you clear the fb.crtc pointer before destroying the fb, then you
> > > > never disable any crtcs in response to removing a framebuffer.
> > > > That's not what we want when the fb is the current fb for the crtc.
> > > >
> > >
> > > Right, there is my missing point. How about this then? Couldn't  we check
> > > this fb is last one or not? And if last one, it makes fb->crtc keep as
> > is.
> >
> > That won't help, It would just make the behaviour of your code
> > identical to the behaviour of the current code.
> >
> > > > > > So it looks like you're making things worse, not better.
> > > > >
> > > > > Anyway I'll just nack the whole idea. What you need to do is track
> the
> > > > > pending page flips, and make sure the old buffer is not freed too
> > > early.
> > > > > Just grab a reference to the old gem object when issuing the page
> flip,
> > > > > and unref it when you're sure the flip has occured. Or you could use
> > > the
> > > > > new drm_framebuffer refcount, but personally I don't see much point
> in
> > > > > that when you already have the gem object refcount at your disposal.
> > > > >
> > > > >
> > > > >
> > > >
> > > > And it seems like that your saying is that each specific drivers
> > > > should resolve this issue(access to invalid memory region) tracking
> the
> > > > pending page flip. But I think that this issue may be a missing point
> drm
> > > > framework missed so specific drivers is processing this instead. And
> with
> > > > this patch, couldn't some codes you mentioned be removed from specific
> > > > drivers? because it doesn't need to track the pending page flips and
> > > > relevant things anymore.
> > > >
> > > > There may be my missing point. I'd welcome to any comments and
> advices.
> > >
> > > If you don't track the page flips somehow, you can't safely free the
> > > previous buffer. It's that simple. You can of course make some of
> > > that code generic enough to be shared between drivers. That is
> > > actually what the drm_flip mechanism that I wrote for atomic page
> > > flips is; a reasonably generic helper for tracking page flips.
> > >
> > >
> > Thanks for comments. Right, now Exynos driver doesn't consider tracking
> > page flips yet. This is my TODO. Anyway couldn't we improve this patch so
> > that drm framework considers such thing?
> 
> > No, because it depends on hardware specific information. The drm core
> > code doesn't have a crystall ball, so it can't just magically know when
> > a page flip completes.
> >
> > > I think with this patch, we could avoid invald memory access issue
> > without
> > > tracking page flips. In this case, pended page flips would be just
> > ignored.
> >
> > I still don't understand how the patch is supposed to help. If you make
> > the proposed change to rmfb() so that it doesn't disable the crtc when
> > you remove a non-current fb, then this would happen:
> >
> > setcrtc(crtc0, fb0)
> >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > pageflip(crtc0, fb1);
> >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > rmfb(fb0);
> >  -> fb0.crtc = NULL;
> >     destroy(fb0);
> >
> > That is exactly the same behaviour we have today, so you still get
> > the invalid memory access to fb0 if the page flip hasn't occured yet.
> > And that means the fb.crtc pointer is entirely pointless.
> >
> >
> I don't think so. let's see it again. below is my idea AND the reason I
> posted this patch.
> Original codes,
> gem alloc(gem0);
> -> gem0 refcount = 1
> gem0 mmap
> -> gem0 refcount = 2
> gem alloc(gem1);
> -> gem1 refcount =1
> gem1 mmap
> -> gem1 refcount =2
> addfb(fb0, gem0);
> -> gem0 refcount=3
> addfb(fb1,gem1);
> -> gem1 refcount = 3
> setcrtc(fb0, crtc0)
> -> crtc0.fb = fb0
> pageflip(crtc0, fb1);
> -> crtc0.fb = fb1.
> and pageflip is repeated
> 
> close(gem0)
> -> gem0 refcount = 2
> close(gem1)
> ->gem1 refcount = 2
> munmap(gem0)
> ->gem0 refcount = 1
> munmap(gem1)
> ->gem1 refcount = 1
> 
> close(drm)
> 1. fb release
> -> check if crtc->fb is same as fb0 but current crtc is pointing to fb1
> 2. so free fb0 without disabling current crtc.
> -> gem0 refcount = 0 so released. At this time, dma access invalid memory
> region unfortunately* *if the dma is accessing gem0.
> 
> 
> 
> With my patch,
> ...
> setcrtc(fb0, crtc0)
> -> crtc0.fb = fb0, fb0.crtc = crtc0
> pageflip(crtc0, fb1);
> -> crtc0.fb = fb1, fb1.crtc = crtc0.
> and pageflip is repeated
> 
> close(gem0)
> -> gem0 refcount = 2
> close(gem1)
> ->gem1 refcount = 2
> munmap(gem0)
> ->gem0 refcount = 1
> munmap(gem1)
> ->gem1 refcount = 1
> close(drm)
> 1. fb release
> -> fb0->crtc is same as crtc so disable crtc (dma stop)

No, that's wrong. The current fb is fb1, so destroying fb0 must not
disable the crtc.

> 2. and free fb0.
> -> gem0 refcount = 0 so released. We can avoid invalid memory access
> because the dma was stopped.
> In addition, one thing you pointed should be considered like below,
> 1. When rmfb is called, if fb is last one then it sets fb->crtc to NULL.
> - the fb is cleaned up with disabling crtc.
> 
> That's all and the issue I faced with.
> 
> I'd happy to give me any comments, opinions.
> 
> Thanks,
> Inki Dae
Inki Dae Nov. 10, 2012, 1:09 a.m. UTC | #16
2012/11/10 Ville Syrjälä <ville.syrjala@linux.intel.com>

> On Sat, Nov 10, 2012 at 01:50:53AM +0900, Inki Dae wrote:
> > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > > On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > >
> > > > > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > >
> > > > > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > > > > This patch fixes access issue to invalid memory region.
> > > > > > > >
> > > > > > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > > > > > cleanup was requested after page flip, it'd try to disable
> > > > > > > > hardware overlay to current crtc.
> > > > > > > > But if current crtc points to another drm_framebuffer,
> > > > > > > > This may induce invalid memory access.
> > > > > > > >
> > > > > > > > Assume that some application are doing page flip with two
> > > > > > > > drm_framebuffer objects. At this time, if second
> drm_framebuffer
> > > > > > > > is set to current crtc and the cleanup to first
> drm_framebuffer
> > > > > > > > is requested once drm release is requested, then first
> > > > > > > > drm_framebuffer would be cleaned up without disabling
> > > > > > > > hardware overlay because current crtc points to second
> > > > > > > > drm_framebuffer. After that, gem buffer to first
> drm_framebuffer
> > > > > > > > would be released and this makes dma access invalid memory
> > > region.
> > > > > > > >
> > > > > > > > This patch adds drm_framebuffer to drm_crtc structure as
> member
> > > > > > >
> > > > > > > That is exactly the reverse of what you're doing in the patch.
> > > > > > > We already have crtc.fb, and you're adding fb.crtc.
> > > > > > >
> > > > > > > > and makes drm_framebuffer_cleanup function check if fb->crtc
> is
> > > > > > > > same as desired fb. And also when setcrtc and pageflip are
> > > > > > > > requested, it makes each drm_framebuffer point to current
> crtc.
> > > > > > >
> > > > > > > Looks like you're just setting the crtc.fb and fb.crtc
> pointers to
> > > > > > > exactly mirror each other in the page flip ioctl. That can't
> fix
> > > > > > > anything.
> > > > > > >
> > > > > > >
> > > > > > At least, when drm is released, the crtc to framebuffer that was
> > > recently
> > > > > > used for scanout can be disabled correctly.
> > > > >
> > > > > Let's take this scenario:
> > > > >
> > > > > setcrtc(crtc0, fb0)
> > > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > > page_flip(crtc0, fb1)
> > > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > > rmfb(fb0)
> > > > >  -> ?
> > > > >
> > > > > The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> > > > > let's consider this just a bug and ignore it for now. So let's
> assume
> > > > > that crtc0 won't be disabled when we call rmfb(fb0).
> > > > >
> > > > >
> > > > Ok, Please assume that my patch includes the below codes. when we
> call
> > > > rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling
> > > crtc.
> > > >
> > > > int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
> > > > *file_priv) {
> > > > ....
> > > > fb->crtc = NULL;
> > > > fb->funcs->destroy(fb);
> > > > out:
> > > > ...
> > > > }
> > >
> > > As stated above, I already assumed that.
> > >
> > > > > The real question is, how would your patch help? You can't free fb0
> > > > > until the page flip has occured, and your patch doesn't track page
> > > > > flips, so how can it do anything useful?
> > > > >
> > > > > > > First of all multiple crtcs can scan out from the same fb, so a
> > > single
> > > > > > > fb.crtc pointer is clearly not enough to represent the
> relationship
> > > > > > > between fbs and crtcs.
> > > > > > >
> > > > > > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > > > > > destroying any framebuffer that was once used for scanout,
> would
> > > > > disable
> > > > > > > some crtc.
> > > > > > >
> > > > > > >
> > > > > > It looks like that you missed my previous comment. Plaese, see
> the
> > > > > previous
> > > > > > comment. And that was already pointed out by Prathyush. fb.crtc
> will
> > > be
> > > > > > cleared at drm_mode_rmfb(). With this, is there my missing
> point?  I
> > > > > really
> > > > > > wonder that with this patch, drm framwork could be faced with any
> > > > > problem.
> > > > >
> > > > > If you clear the fb.crtc pointer before destroying the fb, then you
> > > > > never disable any crtcs in response to removing a framebuffer.
> > > > > That's not what we want when the fb is the current fb for the crtc.
> > > > >
> > > >
> > > > Right, there is my missing point. How about this then? Couldn't  we
> check
> > > > this fb is last one or not? And if last one, it makes fb->crtc keep
> as
> > > is.
> > >
> > > That won't help, It would just make the behaviour of your code
> > > identical to the behaviour of the current code.
> > >
> > > > > > > So it looks like you're making things worse, not better.
> > > > > >
> > > > > > Anyway I'll just nack the whole idea. What you need to do is
> track
> > the
> > > > > > pending page flips, and make sure the old buffer is not freed too
> > > > early.
> > > > > > Just grab a reference to the old gem object when issuing the page
> > flip,
> > > > > > and unref it when you're sure the flip has occured. Or you could
> use
> > > > the
> > > > > > new drm_framebuffer refcount, but personally I don't see much
> point
> > in
> > > > > > that when you already have the gem object refcount at your
> disposal.
> > > > > >
> > > > > >
> > > > > >
> > > > >
> > > > > And it seems like that your saying is that each specific drivers
> > > > > should resolve this issue(access to invalid memory region) tracking
> > the
> > > > > pending page flip. But I think that this issue may be a missing
> point
> > drm
> > > > > framework missed so specific drivers is processing this instead.
> And
> > with
> > > > > this patch, couldn't some codes you mentioned be removed from
> specific
> > > > > drivers? because it doesn't need to track the pending page flips
> and
> > > > > relevant things anymore.
> > > > >
> > > > > There may be my missing point. I'd welcome to any comments and
> > advices.
> > > >
> > > > If you don't track the page flips somehow, you can't safely free the
> > > > previous buffer. It's that simple. You can of course make some of
> > > > that code generic enough to be shared between drivers. That is
> > > > actually what the drm_flip mechanism that I wrote for atomic page
> > > > flips is; a reasonably generic helper for tracking page flips.
> > > >
> > > >
> > > Thanks for comments. Right, now Exynos driver doesn't consider tracking
> > > page flips yet. This is my TODO. Anyway couldn't we improve this patch
> so
> > > that drm framework considers such thing?
> >
> > > No, because it depends on hardware specific information. The drm core
> > > code doesn't have a crystall ball, so it can't just magically know when
> > > a page flip completes.
> > >
> > > > I think with this patch, we could avoid invald memory access issue
> > > without
> > > > tracking page flips. In this case, pended page flips would be just
> > > ignored.
> > >
> > > I still don't understand how the patch is supposed to help. If you make
> > > the proposed change to rmfb() so that it doesn't disable the crtc when
> > > you remove a non-current fb, then this would happen:
> > >
> > > setcrtc(crtc0, fb0)
> > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > pageflip(crtc0, fb1);
> > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > rmfb(fb0);
> > >  -> fb0.crtc = NULL;
> > >     destroy(fb0);
> > >
> > > That is exactly the same behaviour we have today, so you still get
> > > the invalid memory access to fb0 if the page flip hasn't occured yet.
> > > And that means the fb.crtc pointer is entirely pointless.
> > >
> > >
> > I don't think so. let's see it again. below is my idea AND the reason I
> > posted this patch.
> > Original codes,
> > gem alloc(gem0);
> > -> gem0 refcount = 1
> > gem0 mmap
> > -> gem0 refcount = 2
> > gem alloc(gem1);
> > -> gem1 refcount =1
> > gem1 mmap
> > -> gem1 refcount =2
> > addfb(fb0, gem0);
> > -> gem0 refcount=3
> > addfb(fb1,gem1);
> > -> gem1 refcount = 3
> > setcrtc(fb0, crtc0)
> > -> crtc0.fb = fb0
> > pageflip(crtc0, fb1);
> > -> crtc0.fb = fb1.
> > and pageflip is repeated
> >
> > close(gem0)
> > -> gem0 refcount = 2
> > close(gem1)
> > ->gem1 refcount = 2
> > munmap(gem0)
> > ->gem0 refcount = 1
> > munmap(gem1)
> > ->gem1 refcount = 1
> >
> > close(drm)
> > 1. fb release
> > -> check if crtc->fb is same as fb0 but current crtc is pointing to fb1
> > 2. so free fb0 without disabling current crtc.
> > -> gem0 refcount = 0 so released. At this time, dma access invalid memory
> > region unfortunately* *if the dma is accessing gem0.
> >
> >
> >
> > With my patch,
> > ...
> > setcrtc(fb0, crtc0)
> > -> crtc0.fb = fb0, fb0.crtc = crtc0
> > pageflip(crtc0, fb1);
> > -> crtc0.fb = fb1, fb1.crtc = crtc0.
> > and pageflip is repeated
> >
> > close(gem0)
> > -> gem0 refcount = 2
> > close(gem1)
> > ->gem1 refcount = 2
> > munmap(gem0)
> > ->gem0 refcount = 1
> > munmap(gem1)
> > ->gem1 refcount = 1
> > close(drm)
> > 1. fb release
> > -> fb0->crtc is same as crtc so disable crtc (dma stop)
>
> No, that's wrong. The current fb is fb1, so destroying fb0 must not
> disable the crtc.
>
>
Do you think this is wrong that when fb0 is destroyed, the crtc also is
disabled and crtc is pointing to fb1? And in case of Exynos driver,
disabling the crtc would disable hardware overlay so other overlays would
be holded on as is. This is just Exynos case so I don't know how other SoCs
are operated. Could you explain how Desktop side is operated? Maybe there
is my missing point here.

Thanks,
Inki Dae




> > 2. and free fb0.
> > -> gem0 refcount = 0 so released. We can avoid invalid memory access
> > because the dma was stopped.
> > In addition, one thing you pointed should be considered like below,
> > 1. When rmfb is called, if fb is last one then it sets fb->crtc to NULL.
> > - the fb is cleaned up with disabling crtc.
> >
> > That's all and the issue I faced with.
> >
> > I'd happy to give me any comments, opinions.
> >
> > Thanks,
> > Inki Dae
>
> --
> Ville Syrjälä
> Intel OTC
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
Ville Syrjälä Nov. 12, 2012, 12:28 p.m. UTC | #17
On Sat, Nov 10, 2012 at 10:09:02AM +0900, Inki Dae wrote:
> 2012/11/10 Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> > On Sat, Nov 10, 2012 at 01:50:53AM +0900, Inki Dae wrote:
> > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > >
> > > > On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > >
> > > > > > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > >
> > > > > > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > > > > > This patch fixes access issue to invalid memory region.
> > > > > > > > >
> > > > > > > > > crtc had only one drm_framebuffer object so when framebuffer
> > > > > > > > > cleanup was requested after page flip, it'd try to disable
> > > > > > > > > hardware overlay to current crtc.
> > > > > > > > > But if current crtc points to another drm_framebuffer,
> > > > > > > > > This may induce invalid memory access.
> > > > > > > > >
> > > > > > > > > Assume that some application are doing page flip with two
> > > > > > > > > drm_framebuffer objects. At this time, if second
> > drm_framebuffer
> > > > > > > > > is set to current crtc and the cleanup to first
> > drm_framebuffer
> > > > > > > > > is requested once drm release is requested, then first
> > > > > > > > > drm_framebuffer would be cleaned up without disabling
> > > > > > > > > hardware overlay because current crtc points to second
> > > > > > > > > drm_framebuffer. After that, gem buffer to first
> > drm_framebuffer
> > > > > > > > > would be released and this makes dma access invalid memory
> > > > region.
> > > > > > > > >
> > > > > > > > > This patch adds drm_framebuffer to drm_crtc structure as
> > member
> > > > > > > >
> > > > > > > > That is exactly the reverse of what you're doing in the patch.
> > > > > > > > We already have crtc.fb, and you're adding fb.crtc.
> > > > > > > >
> > > > > > > > > and makes drm_framebuffer_cleanup function check if fb->crtc
> > is
> > > > > > > > > same as desired fb. And also when setcrtc and pageflip are
> > > > > > > > > requested, it makes each drm_framebuffer point to current
> > crtc.
> > > > > > > >
> > > > > > > > Looks like you're just setting the crtc.fb and fb.crtc
> > pointers to
> > > > > > > > exactly mirror each other in the page flip ioctl. That can't
> > fix
> > > > > > > > anything.
> > > > > > > >
> > > > > > > >
> > > > > > > At least, when drm is released, the crtc to framebuffer that was
> > > > recently
> > > > > > > used for scanout can be disabled correctly.
> > > > > >
> > > > > > Let's take this scenario:
> > > > > >
> > > > > > setcrtc(crtc0, fb0)
> > > > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > > > page_flip(crtc0, fb1)
> > > > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > > > rmfb(fb0)
> > > > > >  -> ?
> > > > > >
> > > > > > The rmfb() will now disable crtc0 because fb0.crtc == crtc0, but
> > > > > > let's consider this just a bug and ignore it for now. So let's
> > assume
> > > > > > that crtc0 won't be disabled when we call rmfb(fb0).
> > > > > >
> > > > > >
> > > > > Ok, Please assume that my patch includes the below codes. when we
> > call
> > > > > rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without disabling
> > > > crtc.
> > > > >
> > > > > int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file
> > > > > *file_priv) {
> > > > > ....
> > > > > fb->crtc = NULL;
> > > > > fb->funcs->destroy(fb);
> > > > > out:
> > > > > ...
> > > > > }
> > > >
> > > > As stated above, I already assumed that.
> > > >
> > > > > > The real question is, how would your patch help? You can't free fb0
> > > > > > until the page flip has occured, and your patch doesn't track page
> > > > > > flips, so how can it do anything useful?
> > > > > >
> > > > > > > > First of all multiple crtcs can scan out from the same fb, so a
> > > > single
> > > > > > > > fb.crtc pointer is clearly not enough to represent the
> > relationship
> > > > > > > > between fbs and crtcs.
> > > > > > > >
> > > > > > > > Also you're not clearing the fb.crtc pointer anywhere, so now
> > > > > > > > destroying any framebuffer that was once used for scanout,
> > would
> > > > > > disable
> > > > > > > > some crtc.
> > > > > > > >
> > > > > > > >
> > > > > > > It looks like that you missed my previous comment. Plaese, see
> > the
> > > > > > previous
> > > > > > > comment. And that was already pointed out by Prathyush. fb.crtc
> > will
> > > > be
> > > > > > > cleared at drm_mode_rmfb(). With this, is there my missing
> > point?  I
> > > > > > really
> > > > > > > wonder that with this patch, drm framwork could be faced with any
> > > > > > problem.
> > > > > >
> > > > > > If you clear the fb.crtc pointer before destroying the fb, then you
> > > > > > never disable any crtcs in response to removing a framebuffer.
> > > > > > That's not what we want when the fb is the current fb for the crtc.
> > > > > >
> > > > >
> > > > > Right, there is my missing point. How about this then? Couldn't  we
> > check
> > > > > this fb is last one or not? And if last one, it makes fb->crtc keep
> > as
> > > > is.
> > > >
> > > > That won't help, It would just make the behaviour of your code
> > > > identical to the behaviour of the current code.
> > > >
> > > > > > > > So it looks like you're making things worse, not better.
> > > > > > >
> > > > > > > Anyway I'll just nack the whole idea. What you need to do is
> > track
> > > the
> > > > > > > pending page flips, and make sure the old buffer is not freed too
> > > > > early.
> > > > > > > Just grab a reference to the old gem object when issuing the page
> > > flip,
> > > > > > > and unref it when you're sure the flip has occured. Or you could
> > use
> > > > > the
> > > > > > > new drm_framebuffer refcount, but personally I don't see much
> > point
> > > in
> > > > > > > that when you already have the gem object refcount at your
> > disposal.
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > >
> > > > > > And it seems like that your saying is that each specific drivers
> > > > > > should resolve this issue(access to invalid memory region) tracking
> > > the
> > > > > > pending page flip. But I think that this issue may be a missing
> > point
> > > drm
> > > > > > framework missed so specific drivers is processing this instead.
> > And
> > > with
> > > > > > this patch, couldn't some codes you mentioned be removed from
> > specific
> > > > > > drivers? because it doesn't need to track the pending page flips
> > and
> > > > > > relevant things anymore.
> > > > > >
> > > > > > There may be my missing point. I'd welcome to any comments and
> > > advices.
> > > > >
> > > > > If you don't track the page flips somehow, you can't safely free the
> > > > > previous buffer. It's that simple. You can of course make some of
> > > > > that code generic enough to be shared between drivers. That is
> > > > > actually what the drm_flip mechanism that I wrote for atomic page
> > > > > flips is; a reasonably generic helper for tracking page flips.
> > > > >
> > > > >
> > > > Thanks for comments. Right, now Exynos driver doesn't consider tracking
> > > > page flips yet. This is my TODO. Anyway couldn't we improve this patch
> > so
> > > > that drm framework considers such thing?
> > >
> > > > No, because it depends on hardware specific information. The drm core
> > > > code doesn't have a crystall ball, so it can't just magically know when
> > > > a page flip completes.
> > > >
> > > > > I think with this patch, we could avoid invald memory access issue
> > > > without
> > > > > tracking page flips. In this case, pended page flips would be just
> > > > ignored.
> > > >
> > > > I still don't understand how the patch is supposed to help. If you make
> > > > the proposed change to rmfb() so that it doesn't disable the crtc when
> > > > you remove a non-current fb, then this would happen:
> > > >
> > > > setcrtc(crtc0, fb0)
> > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > pageflip(crtc0, fb1);
> > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > rmfb(fb0);
> > > >  -> fb0.crtc = NULL;
> > > >     destroy(fb0);
> > > >
> > > > That is exactly the same behaviour we have today, so you still get
> > > > the invalid memory access to fb0 if the page flip hasn't occured yet.
> > > > And that means the fb.crtc pointer is entirely pointless.
> > > >
> > > >
> > > I don't think so. let's see it again. below is my idea AND the reason I
> > > posted this patch.
> > > Original codes,
> > > gem alloc(gem0);
> > > -> gem0 refcount = 1
> > > gem0 mmap
> > > -> gem0 refcount = 2
> > > gem alloc(gem1);
> > > -> gem1 refcount =1
> > > gem1 mmap
> > > -> gem1 refcount =2
> > > addfb(fb0, gem0);
> > > -> gem0 refcount=3
> > > addfb(fb1,gem1);
> > > -> gem1 refcount = 3
> > > setcrtc(fb0, crtc0)
> > > -> crtc0.fb = fb0
> > > pageflip(crtc0, fb1);
> > > -> crtc0.fb = fb1.
> > > and pageflip is repeated
> > >
> > > close(gem0)
> > > -> gem0 refcount = 2
> > > close(gem1)
> > > ->gem1 refcount = 2
> > > munmap(gem0)
> > > ->gem0 refcount = 1
> > > munmap(gem1)
> > > ->gem1 refcount = 1
> > >
> > > close(drm)
> > > 1. fb release
> > > -> check if crtc->fb is same as fb0 but current crtc is pointing to fb1
> > > 2. so free fb0 without disabling current crtc.
> > > -> gem0 refcount = 0 so released. At this time, dma access invalid memory
> > > region unfortunately* *if the dma is accessing gem0.
> > >
> > >
> > >
> > > With my patch,
> > > ...
> > > setcrtc(fb0, crtc0)
> > > -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > pageflip(crtc0, fb1);
> > > -> crtc0.fb = fb1, fb1.crtc = crtc0.
> > > and pageflip is repeated
> > >
> > > close(gem0)
> > > -> gem0 refcount = 2
> > > close(gem1)
> > > ->gem1 refcount = 2
> > > munmap(gem0)
> > > ->gem0 refcount = 1
> > > munmap(gem1)
> > > ->gem1 refcount = 1
> > > close(drm)
> > > 1. fb release
> > > -> fb0->crtc is same as crtc so disable crtc (dma stop)
> >
> > No, that's wrong. The current fb is fb1, so destroying fb0 must not
> > disable the crtc.
> >
> >
> Do you think this is wrong that when fb0 is destroyed, the crtc also is
> disabled and crtc is pointing to fb1?

Yes. You must disable the crtc if and only if you destroy the current
fb. Current fb means the fb specified by the latest setcrtc or pageflip
ioctl. Destroying any other fb must not affect the crtc. And this is
exactly what the current code does.

> And in case of Exynos driver,
> disabling the crtc would disable hardware overlay so other overlays would
> be holded on as is. This is just Exynos case so I don't know how other SoCs
> are operated. Could you explain how Desktop side is operated? Maybe there
> is my missing point here.

I'm not sure what you're asking.

Are you asking how other drivers know know when it's safe to free the
memory? Well, the usual method is to track the page flips via some
suitable interrupts. A reference is kept to the memory until it's safe
to get rid of it.

Or are you asking what should happen when a crtc is disabled? Then my
answer is that when a crtc is disabled, there must be no output signal
whatsoever.

It's an unfortunate design bug that we even have the fb->crtc link.
Ideally it would not exist, and all scanout duties would be handled
by planes. Then destroying fbs would never disable the crtc completely,
just the planes scanning from those fbs.
Inki Dae Nov. 12, 2012, 2:36 p.m. UTC | #18
2012/11/12 Ville Syrjälä <ville.syrjala@linux.intel.com>

> On Sat, Nov 10, 2012 at 10:09:02AM +0900, Inki Dae wrote:
> > 2012/11/10 Ville Syrjälä <ville.syrjala@linux.intel.com>
> >
> > > On Sat, Nov 10, 2012 at 01:50:53AM +0900, Inki Dae wrote:
> > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > >
> > > > > On Fri, Nov 09, 2012 at 11:04:58PM +0900, Inki Dae wrote:
> > > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > >
> > > > > > > On Fri, Nov 09, 2012 at 09:41:19PM +0900, Inki Dae wrote:
> > > > > > > > 2012/11/9 Ville Syrjälä <ville.syrjala@linux.intel.com>
> > > > > > > >
> > > > > > > > > On Fri, Nov 09, 2012 at 04:39:30PM +0900, Inki Dae wrote:
> > > > > > > > > > This patch fixes access issue to invalid memory region.
> > > > > > > > > >
> > > > > > > > > > crtc had only one drm_framebuffer object so when
> framebuffer
> > > > > > > > > > cleanup was requested after page flip, it'd try to
> disable
> > > > > > > > > > hardware overlay to current crtc.
> > > > > > > > > > But if current crtc points to another drm_framebuffer,
> > > > > > > > > > This may induce invalid memory access.
> > > > > > > > > >
> > > > > > > > > > Assume that some application are doing page flip with two
> > > > > > > > > > drm_framebuffer objects. At this time, if second
> > > drm_framebuffer
> > > > > > > > > > is set to current crtc and the cleanup to first
> > > drm_framebuffer
> > > > > > > > > > is requested once drm release is requested, then first
> > > > > > > > > > drm_framebuffer would be cleaned up without disabling
> > > > > > > > > > hardware overlay because current crtc points to second
> > > > > > > > > > drm_framebuffer. After that, gem buffer to first
> > > drm_framebuffer
> > > > > > > > > > would be released and this makes dma access invalid
> memory
> > > > > region.
> > > > > > > > > >
> > > > > > > > > > This patch adds drm_framebuffer to drm_crtc structure as
> > > member
> > > > > > > > >
> > > > > > > > > That is exactly the reverse of what you're doing in the
> patch.
> > > > > > > > > We already have crtc.fb, and you're adding fb.crtc.
> > > > > > > > >
> > > > > > > > > > and makes drm_framebuffer_cleanup function check if
> fb->crtc
> > > is
> > > > > > > > > > same as desired fb. And also when setcrtc and pageflip
> are
> > > > > > > > > > requested, it makes each drm_framebuffer point to current
> > > crtc.
> > > > > > > > >
> > > > > > > > > Looks like you're just setting the crtc.fb and fb.crtc
> > > pointers to
> > > > > > > > > exactly mirror each other in the page flip ioctl. That
> can't
> > > fix
> > > > > > > > > anything.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > At least, when drm is released, the crtc to framebuffer that
> was
> > > > > recently
> > > > > > > > used for scanout can be disabled correctly.
> > > > > > >
> > > > > > > Let's take this scenario:
> > > > > > >
> > > > > > > setcrtc(crtc0, fb0)
> > > > > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > > > > page_flip(crtc0, fb1)
> > > > > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > > > > rmfb(fb0)
> > > > > > >  -> ?
> > > > > > >
> > > > > > > The rmfb() will now disable crtc0 because fb0.crtc == crtc0,
> but
> > > > > > > let's consider this just a bug and ignore it for now. So let's
> > > assume
> > > > > > > that crtc0 won't be disabled when we call rmfb(fb0).
> > > > > > >
> > > > > > >
> > > > > > Ok, Please assume that my patch includes the below codes. when we
> > > call
> > > > > > rmfb(fb0), fb0->crtc is NULL. so fb0 will be freed without
> disabling
> > > > > crtc.
> > > > > >
> > > > > > int drm_mode_rmfb(struct drm_device *dev, void *data, struct
> drm_file
> > > > > > *file_priv) {
> > > > > > ....
> > > > > > fb->crtc = NULL;
> > > > > > fb->funcs->destroy(fb);
> > > > > > out:
> > > > > > ...
> > > > > > }
> > > > >
> > > > > As stated above, I already assumed that.
> > > > >
> > > > > > > The real question is, how would your patch help? You can't
> free fb0
> > > > > > > until the page flip has occured, and your patch doesn't track
> page
> > > > > > > flips, so how can it do anything useful?
> > > > > > >
> > > > > > > > > First of all multiple crtcs can scan out from the same fb,
> so a
> > > > > single
> > > > > > > > > fb.crtc pointer is clearly not enough to represent the
> > > relationship
> > > > > > > > > between fbs and crtcs.
> > > > > > > > >
> > > > > > > > > Also you're not clearing the fb.crtc pointer anywhere, so
> now
> > > > > > > > > destroying any framebuffer that was once used for scanout,
> > > would
> > > > > > > disable
> > > > > > > > > some crtc.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > It looks like that you missed my previous comment. Plaese,
> see
> > > the
> > > > > > > previous
> > > > > > > > comment. And that was already pointed out by Prathyush.
> fb.crtc
> > > will
> > > > > be
> > > > > > > > cleared at drm_mode_rmfb(). With this, is there my missing
> > > point?  I
> > > > > > > really
> > > > > > > > wonder that with this patch, drm framwork could be faced
> with any
> > > > > > > problem.
> > > > > > >
> > > > > > > If you clear the fb.crtc pointer before destroying the fb,
> then you
> > > > > > > never disable any crtcs in response to removing a framebuffer.
> > > > > > > That's not what we want when the fb is the current fb for the
> crtc.
> > > > > > >
> > > > > >
> > > > > > Right, there is my missing point. How about this then? Couldn't
>  we
> > > check
> > > > > > this fb is last one or not? And if last one, it makes fb->crtc
> keep
> > > as
> > > > > is.
> > > > >
> > > > > That won't help, It would just make the behaviour of your code
> > > > > identical to the behaviour of the current code.
> > > > >
> > > > > > > > > So it looks like you're making things worse, not better.
> > > > > > > >
> > > > > > > > Anyway I'll just nack the whole idea. What you need to do is
> > > track
> > > > the
> > > > > > > > pending page flips, and make sure the old buffer is not
> freed too
> > > > > > early.
> > > > > > > > Just grab a reference to the old gem object when issuing the
> page
> > > > flip,
> > > > > > > > and unref it when you're sure the flip has occured. Or you
> could
> > > use
> > > > > > the
> > > > > > > > new drm_framebuffer refcount, but personally I don't see much
> > > point
> > > > in
> > > > > > > > that when you already have the gem object refcount at your
> > > disposal.
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > > > And it seems like that your saying is that each specific
> drivers
> > > > > > > should resolve this issue(access to invalid memory region)
> tracking
> > > > the
> > > > > > > pending page flip. But I think that this issue may be a missing
> > > point
> > > > drm
> > > > > > > framework missed so specific drivers is processing this
> instead.
> > > And
> > > > with
> > > > > > > this patch, couldn't some codes you mentioned be removed from
> > > specific
> > > > > > > drivers? because it doesn't need to track the pending page
> flips
> > > and
> > > > > > > relevant things anymore.
> > > > > > >
> > > > > > > There may be my missing point. I'd welcome to any comments and
> > > > advices.
> > > > > >
> > > > > > If you don't track the page flips somehow, you can't safely free
> the
> > > > > > previous buffer. It's that simple. You can of course make some of
> > > > > > that code generic enough to be shared between drivers. That is
> > > > > > actually what the drm_flip mechanism that I wrote for atomic page
> > > > > > flips is; a reasonably generic helper for tracking page flips.
> > > > > >
> > > > > >
> > > > > Thanks for comments. Right, now Exynos driver doesn't consider
> tracking
> > > > > page flips yet. This is my TODO. Anyway couldn't we improve this
> patch
> > > so
> > > > > that drm framework considers such thing?
> > > >
> > > > > No, because it depends on hardware specific information. The drm
> core
> > > > > code doesn't have a crystall ball, so it can't just magically know
> when
> > > > > a page flip completes.
> > > > >
> > > > > > I think with this patch, we could avoid invald memory access
> issue
> > > > > without
> > > > > > tracking page flips. In this case, pended page flips would be
> just
> > > > > ignored.
> > > > >
> > > > > I still don't understand how the patch is supposed to help. If you
> make
> > > > > the proposed change to rmfb() so that it doesn't disable the crtc
> when
> > > > > you remove a non-current fb, then this would happen:
> > > > >
> > > > > setcrtc(crtc0, fb0)
> > > > >  -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > > pageflip(crtc0, fb1);
> > > > >  -> crtc0.fb = fb1, fb1.crtc = crtc0
> > > > > rmfb(fb0);
> > > > >  -> fb0.crtc = NULL;
> > > > >     destroy(fb0);
> > > > >
> > > > > That is exactly the same behaviour we have today, so you still get
> > > > > the invalid memory access to fb0 if the page flip hasn't occured
> yet.
> > > > > And that means the fb.crtc pointer is entirely pointless.
> > > > >
> > > > >
> > > > I don't think so. let's see it again. below is my idea AND the
> reason I
> > > > posted this patch.
> > > > Original codes,
> > > > gem alloc(gem0);
> > > > -> gem0 refcount = 1
> > > > gem0 mmap
> > > > -> gem0 refcount = 2
> > > > gem alloc(gem1);
> > > > -> gem1 refcount =1
> > > > gem1 mmap
> > > > -> gem1 refcount =2
> > > > addfb(fb0, gem0);
> > > > -> gem0 refcount=3
> > > > addfb(fb1,gem1);
> > > > -> gem1 refcount = 3
> > > > setcrtc(fb0, crtc0)
> > > > -> crtc0.fb = fb0
> > > > pageflip(crtc0, fb1);
> > > > -> crtc0.fb = fb1.
> > > > and pageflip is repeated
> > > >
> > > > close(gem0)
> > > > -> gem0 refcount = 2
> > > > close(gem1)
> > > > ->gem1 refcount = 2
> > > > munmap(gem0)
> > > > ->gem0 refcount = 1
> > > > munmap(gem1)
> > > > ->gem1 refcount = 1
> > > >
> > > > close(drm)
> > > > 1. fb release
> > > > -> check if crtc->fb is same as fb0 but current crtc is pointing to
> fb1
> > > > 2. so free fb0 without disabling current crtc.
> > > > -> gem0 refcount = 0 so released. At this time, dma access invalid
> memory
> > > > region unfortunately* *if the dma is accessing gem0.
> > > >
> > > >
> > > >
> > > > With my patch,
> > > > ...
> > > > setcrtc(fb0, crtc0)
> > > > -> crtc0.fb = fb0, fb0.crtc = crtc0
> > > > pageflip(crtc0, fb1);
> > > > -> crtc0.fb = fb1, fb1.crtc = crtc0.
> > > > and pageflip is repeated
> > > >
> > > > close(gem0)
> > > > -> gem0 refcount = 2
> > > > close(gem1)
> > > > ->gem1 refcount = 2
> > > > munmap(gem0)
> > > > ->gem0 refcount = 1
> > > > munmap(gem1)
> > > > ->gem1 refcount = 1
> > > > close(drm)
> > > > 1. fb release
> > > > -> fb0->crtc is same as crtc so disable crtc (dma stop)
> > >
> > > No, that's wrong. The current fb is fb1, so destroying fb0 must not
> > > disable the crtc.
> > >
> > >
> > Do you think this is wrong that when fb0 is destroyed, the crtc also is
> > disabled and crtc is pointing to fb1?
>
> Yes. You must disable the crtc if and only if you destroy the current
> fb. Current fb means the fb specified by the latest setcrtc or pageflip
> ioctl. Destroying any other fb must not affect the crtc. And this is
> exactly what the current code does.
>
>
Right, I realized that there was my missing point though your comment. I
thought that current fb would be set to crtc->fb only if setmode request.
But page flip request also did it(by specific driver's page flip
function). Now Exynos driver has one problem. that is to set current fb to
crtc->fb after drm_vblank_get call . This could induce invalid memory
access like below,

crtc0's current fb is fb0

page flip request(crtc0, fb1)
1. drm_vblank_get call
2. vsync occurs and the dma access memory region to fb0 yet.
3. crtc0->fb = fb1
3. drm is released
4. crtc's current fb is fb1 but the dma is accessing memory region to fb0
yet because vsync doesn't occur so fb0 doesn't disable crtc and releses its
own gem buffer. This would induce the problem and definitely is a bug.

For this, I will fix it as soon as possible after testing.



> > And in case of Exynos driver,
> > disabling the crtc would disable hardware overlay so other overlays would
> > be holded on as is. This is just Exynos case so I don't know how other
> SoCs
> > are operated. Could you explain how Desktop side is operated? Maybe there
> > is my missing point here.
>
> I'm not sure what you're asking.
>
>

> Are you asking how other drivers know know when it's safe to free the
> memory? Well, the usual method is to track the page flips via some
> suitable interrupts. A reference is kept to the memory until it's safe
> to get rid of it.
>
> Or are you asking what should happen when a crtc is disabled? Then my
> answer is that when a crtc is disabled, there must be no output signal
> whatsoever.
>
> It's an unfortunate design bug that we even have the fb->crtc link.
> Ideally it would not exist, and all scanout duties would be handled
> by planes. Then destroying fbs would never disable the crtc completely,
> just the planes scanning from those fbs.
>
>
Understood. And I'd like to say thank you again. Your comment is very
helpful to me.

Thanks,
Inki Dae


> --
> Ville Syrjälä
> Intel OTC
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
>
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index ef1b221..5c04bd4 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -386,7 +386,7 @@  void drm_framebuffer_remove(struct drm_framebuffer *fb)
 
 	/* remove from any CRTC */
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		if (crtc->fb == fb) {
+		if (fb->crtc == crtc) {
 			/* should turn off the crtc */
 			memset(&set, 0, sizeof(struct drm_mode_set));
 			set.crtc = crtc;
@@ -2027,6 +2027,7 @@  int drm_mode_setcrtc(struct drm_device *dev, void *data,
 	set.mode = mode;
 	set.connectors = connector_set;
 	set.num_connectors = crtc_req->count_connectors;
+	fb->crtc = crtc;
 	set.fb = fb;
 	ret = crtc->funcs->set_config(&set);
 
@@ -3635,8 +3636,8 @@  int drm_mode_page_flip_ioctl(struct drm_device *dev,
 			spin_unlock_irqrestore(&dev->event_lock, flags);
 			kfree(e);
 		}
-	}
-
+	} else
+		fb->crtc = crtc;
 out:
 	mutex_unlock(&dev->mode_config.mutex);
 	return ret;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 3fa18b7..92889be 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -256,6 +256,7 @@  struct drm_framebuffer {
 	struct kref refcount;
 	struct list_head head;
 	struct drm_mode_object base;
+	struct drm_crtc	*crtc;
 	const struct drm_framebuffer_funcs *funcs;
 	unsigned int pitches[4];
 	unsigned int offsets[4];