media: entity: skip non-data link when removing reverse links

Message ID 20220412062313.1645762-1-yunkec@google.com (mailing list archive)
State Accepted
Headers
Series media: entity: skip non-data link when removing reverse links |

Commit Message

Yunke Cao April 12, 2022, 6:23 a.m. UTC
  The original implementation removes reverse links for any input link and
assumes the presense of sink/source.
It fails when the link is a not a data link.
media_entity_remove_links when there's an ancillary link can also fail.

We only need to remove reverse links for a data link.

Signed-off-by: Yunke Cao <yunkec@google.com>
---
 drivers/media/mc/mc-entity.c | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)
  

Comments

Sakari Ailus April 14, 2022, 6:18 a.m. UTC | #1
Hi Yunke,

Thanks for the patch.

On Tue, Apr 12, 2022 at 03:23:13PM +0900, Yunke Cao wrote:
> The original implementation removes reverse links for any input link and
> assumes the presense of sink/source.
> It fails when the link is a not a data link.
> media_entity_remove_links when there's an ancillary link can also fail.

The function's return type is void. Are there other adverse effects from
this? Looking at the function, it would seem like that the reverse link
simply isn't found in this case, and so not removed.

> 
> We only need to remove reverse links for a data link.

Ideally this would not be based on the link flags as it's not a very robust
way to test whather a backlink needs to be removed.

> 
> Signed-off-by: Yunke Cao <yunkec@google.com>
> ---
>  drivers/media/mc/mc-entity.c | 34 +++++++++++++++++++---------------
>  1 file changed, 19 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
> index 1ff60d411ea9..11f5207f73aa 100644
> --- a/drivers/media/mc/mc-entity.c
> +++ b/drivers/media/mc/mc-entity.c
> @@ -597,26 +597,30 @@ static void __media_entity_remove_link(struct media_entity *entity,
>  	struct media_link *rlink, *tmp;
>  	struct media_entity *remote;
>  
> -	if (link->source->entity == entity)
> -		remote = link->sink->entity;
> -	else
> -		remote = link->source->entity;
> +	/* Remove the reverse links for a data link. */
> +	if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {
> +		if (link->source->entity == entity)
> +			remote = link->sink->entity;
> +		else
> +			remote = link->source->entity;
>  
> -	list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
> -		if (rlink != link->reverse)
> -			continue;
> +		list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
> +			if (rlink != link->reverse)
> +				continue;
>  
> -		if (link->source->entity == entity)
> -			remote->num_backlinks--;
> +			if (link->source->entity == entity)
> +				remote->num_backlinks--;
>  
> -		/* Remove the remote link */
> -		list_del(&rlink->list);
> -		media_gobj_destroy(&rlink->graph_obj);
> -		kfree(rlink);
> +			/* Remove the remote link */
> +			list_del(&rlink->list);
> +			media_gobj_destroy(&rlink->graph_obj);
> +			kfree(rlink);
>  
> -		if (--remote->num_links == 0)
> -			break;
> +			if (--remote->num_links == 0)
> +				break;
> +		}
>  	}
> +
>  	list_del(&link->list);
>  	media_gobj_destroy(&link->graph_obj);
>  	kfree(link);
  
Yunke Cao April 14, 2022, 6:44 a.m. UTC | #2
Hi Sakari!

Thanks for the review. See my reply inline.

On Thu, Apr 14, 2022 at 3:19 PM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
>
> Hi Yunke,
>
> Thanks for the patch.
>
> On Tue, Apr 12, 2022 at 03:23:13PM +0900, Yunke Cao wrote:
> > The original implementation removes reverse links for any input link and
> > assumes the presense of sink/source.
> > It fails when the link is a not a data link.
> > media_entity_remove_links when there's an ancillary link can also fail.
>
> The function's return type is void. Are there other adverse effects from
> this? Looking at the function, it would seem like that the reverse link
> simply isn't found in this case, and so not removed.
>
The function dereferences without any check link->source and link->sink
("link->source->entity == entity" etc.), which is in union.
Ancillary links populate gobj0/gobj1 instead of source/sink.
Calling this function on ancillary links can cause crashes.
> >
> > We only need to remove reverse links for a data link.
>
> Ideally this would not be based on the link flags as it's not a very robust
> way to test whather a backlink needs to be removed.
>
I was mainly trying to make sure link->source and link->sink are
populated by checking the link type.
Currently, only data links need to run this part of the code to remove
reverse links so I feel this is
the easiest way. Let me know if there's any better alternative.
> >
> > Signed-off-by: Yunke Cao <yunkec@google.com>
> > ---
> >  drivers/media/mc/mc-entity.c | 34 +++++++++++++++++++---------------
> >  1 file changed, 19 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
> > index 1ff60d411ea9..11f5207f73aa 100644
> > --- a/drivers/media/mc/mc-entity.c
> > +++ b/drivers/media/mc/mc-entity.c
> > @@ -597,26 +597,30 @@ static void __media_entity_remove_link(struct media_entity *entity,
> >       struct media_link *rlink, *tmp;
> >       struct media_entity *remote;
> >
> > -     if (link->source->entity == entity)
> > -             remote = link->sink->entity;
> > -     else
> > -             remote = link->source->entity;
> > +     /* Remove the reverse links for a data link. */
> > +     if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {
> > +             if (link->source->entity == entity)
> > +                     remote = link->sink->entity;
> > +             else
> > +                     remote = link->source->entity;
> >
> > -     list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
> > -             if (rlink != link->reverse)
> > -                     continue;
> > +             list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
> > +                     if (rlink != link->reverse)
> > +                             continue;
> >
> > -             if (link->source->entity == entity)
> > -                     remote->num_backlinks--;
> > +                     if (link->source->entity == entity)
> > +                             remote->num_backlinks--;
> >
> > -             /* Remove the remote link */
> > -             list_del(&rlink->list);
> > -             media_gobj_destroy(&rlink->graph_obj);
> > -             kfree(rlink);
> > +                     /* Remove the remote link */
> > +                     list_del(&rlink->list);
> > +                     media_gobj_destroy(&rlink->graph_obj);
> > +                     kfree(rlink);
> >
> > -             if (--remote->num_links == 0)
> > -                     break;
> > +                     if (--remote->num_links == 0)
> > +                             break;
> > +             }
> >       }
> > +
> >       list_del(&link->list);
> >       media_gobj_destroy(&link->graph_obj);
> >       kfree(link);
>
> --
> Kind regards,
>
> Sakari Ailus

Thanks!
Yunke
  
Sakari Ailus April 14, 2022, 9:37 a.m. UTC | #3
Hi Yunke,

On Thu, Apr 14, 2022 at 03:44:47PM +0900, Yunke Cao wrote:
> On Thu, Apr 14, 2022 at 3:19 PM Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
> >
> > Hi Yunke,
> >
> > Thanks for the patch.
> >
> > On Tue, Apr 12, 2022 at 03:23:13PM +0900, Yunke Cao wrote:
> > > The original implementation removes reverse links for any input link and
> > > assumes the presense of sink/source.
> > > It fails when the link is a not a data link.
> > > media_entity_remove_links when there's an ancillary link can also fail.
> >
> > The function's return type is void. Are there other adverse effects from
> > this? Looking at the function, it would seem like that the reverse link
> > simply isn't found in this case, and so not removed.
> >
> The function dereferences without any check link->source and link->sink
> ("link->source->entity == entity" etc.), which is in union.
> Ancillary links populate gobj0/gobj1 instead of source/sink.
> Calling this function on ancillary links can cause crashes.

Indeed. The struct definition isn't too pretty either. And this only works
with interface links as they're not included in the links list...

> > >
> > > We only need to remove reverse links for a data link.
> >
> > Ideally this would not be based on the link flags as it's not a very robust
> > way to test whather a backlink needs to be removed.
> >
> I was mainly trying to make sure link->source and link->sink are
> populated by checking the link type.
> Currently, only data links need to run this part of the code to remove
> reverse links so I feel this is
> the easiest way. Let me know if there's any better alternative.

I think we should get this in now. The code seems to be in a dire need for
cleanup still.
  

Patch

diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
index 1ff60d411ea9..11f5207f73aa 100644
--- a/drivers/media/mc/mc-entity.c
+++ b/drivers/media/mc/mc-entity.c
@@ -597,26 +597,30 @@  static void __media_entity_remove_link(struct media_entity *entity,
 	struct media_link *rlink, *tmp;
 	struct media_entity *remote;
 
-	if (link->source->entity == entity)
-		remote = link->sink->entity;
-	else
-		remote = link->source->entity;
+	/* Remove the reverse links for a data link. */
+	if ((link->flags & MEDIA_LNK_FL_LINK_TYPE) == MEDIA_LNK_FL_DATA_LINK) {
+		if (link->source->entity == entity)
+			remote = link->sink->entity;
+		else
+			remote = link->source->entity;
 
-	list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
-		if (rlink != link->reverse)
-			continue;
+		list_for_each_entry_safe(rlink, tmp, &remote->links, list) {
+			if (rlink != link->reverse)
+				continue;
 
-		if (link->source->entity == entity)
-			remote->num_backlinks--;
+			if (link->source->entity == entity)
+				remote->num_backlinks--;
 
-		/* Remove the remote link */
-		list_del(&rlink->list);
-		media_gobj_destroy(&rlink->graph_obj);
-		kfree(rlink);
+			/* Remove the remote link */
+			list_del(&rlink->list);
+			media_gobj_destroy(&rlink->graph_obj);
+			kfree(rlink);
 
-		if (--remote->num_links == 0)
-			break;
+			if (--remote->num_links == 0)
+				break;
+		}
 	}
+
 	list_del(&link->list);
 	media_gobj_destroy(&link->graph_obj);
 	kfree(link);