From 7349cec14d63251d093a213f7d40ed3c732b3734 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Thu, 9 May 2013 08:29:32 -0300 Subject: [PATCH] [media] media: Add a function removing all links of a media entity This function allows to remove all media entity's links to other entities, leaving no references to a media entity's links array at its remote entities. Currently, when a driver of some entity is removed it will free its media entities links[] array, leaving dangling pointers at other entities that are part of same media graph. This is troublesome when drivers of a media device entities are in separate kernel modules, removing only some modules will leave others in an incorrect state. This function is intended to be used when an entity is being unregistered from a media device. With an assumption that normally the media links should be created between media entities registered to a media device, with the graph mutex held. Signed-off-by: Sylwester Nawrocki Reviewed-by: Andrzej Hajda Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/media-entity.c | 50 ++++++++++++++++++++++++++++++++++++ include/media/media-entity.h | 3 +++ 2 files changed, 53 insertions(+) diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index df72f7d99d80..cb30ffbd5ba8 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -429,6 +429,56 @@ media_entity_create_link(struct media_entity *source, u16 source_pad, } EXPORT_SYMBOL_GPL(media_entity_create_link); +void __media_entity_remove_links(struct media_entity *entity) +{ + unsigned int i; + + for (i = 0; i < entity->num_links; i++) { + struct media_link *link = &entity->links[i]; + struct media_entity *remote; + unsigned int r = 0; + + if (link->source->entity == entity) + remote = link->sink->entity; + else + remote = link->source->entity; + + while (r < remote->num_links) { + struct media_link *rlink = &remote->links[r]; + + if (rlink != link->reverse) { + r++; + continue; + } + + if (link->source->entity == entity) + remote->num_backlinks--; + + if (--remote->num_links == 0) + break; + + /* Insert last entry in place of the dropped link. */ + *rlink = remote->links[remote->num_links]; + } + } + + entity->num_links = 0; + entity->num_backlinks = 0; +} +EXPORT_SYMBOL_GPL(__media_entity_remove_links); + +void media_entity_remove_links(struct media_entity *entity) +{ + /* Do nothing if the entity is not registered. */ + if (entity->parent == NULL) + return; + + mutex_lock(&entity->parent->graph_mutex); + __media_entity_remove_links(entity); + mutex_unlock(&entity->parent->graph_mutex); +} +EXPORT_SYMBOL_GPL(media_entity_remove_links); + static int __media_entity_setup_link_notify(struct media_link *link, u32 flags) { int ret; diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 4eefedcaa66d..06bacf937d61 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -128,6 +128,9 @@ void media_entity_cleanup(struct media_entity *entity); int media_entity_create_link(struct media_entity *source, u16 source_pad, struct media_entity *sink, u16 sink_pad, u32 flags); +void __media_entity_remove_links(struct media_entity *entity); +void media_entity_remove_links(struct media_entity *entity); + int __media_entity_setup_link(struct media_link *link, u32 flags); int media_entity_setup_link(struct media_link *link, u32 flags); struct media_link *media_entity_find_link(struct media_pad *source, -- 2.39.5