aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rpmsg
diff options
context:
space:
mode:
authorOhad Ben-Cohen <ohad@wizery.com>2012-06-06 03:09:25 -0400
committerOhad Ben-Cohen <ohad@wizery.com>2012-07-04 04:51:42 -0400
commit5a081caa0414b9bbb82c17ffab9d6fe66edbb72f (patch)
treede3c3d2044db5843e836690732a4f688985e35af /drivers/rpmsg
parent6887a4131da3adaab011613776d865f4bcfb5678 (diff)
rpmsg: avoid premature deallocation of endpoints
When an inbound message arrives, the rpmsg core looks up its associated endpoint and invokes the registered callback. If a message arrives while its endpoint is being removed (because the rpmsg driver was removed, or a recovery of a remote processor has kicked in) we must ensure atomicity, i.e.: - Either the ept is removed before it is found or - The ept is found but will not be freed until the callback returns This is achieved by maintaining a per-ept reference count, which, when drops to zero, will trigger deallocation of the ept. With this in hand, it is now forbidden to directly deallocate epts once they have been added to the endpoints idr. Cc: stable <stable@vger.kernel.org> Reported-by: Fernando Guzman Lugo <fernando.lugo@ti.com> Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
Diffstat (limited to 'drivers/rpmsg')
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 75506ec2840e..9623327ba509 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -188,6 +188,26 @@ static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
188 rpdev->id.name); 188 rpdev->id.name);
189} 189}
190 190
191/**
192 * __ept_release() - deallocate an rpmsg endpoint
193 * @kref: the ept's reference count
194 *
195 * This function deallocates an ept, and is invoked when its @kref refcount
196 * drops to zero.
197 *
198 * Never invoke this function directly!
199 */
200static void __ept_release(struct kref *kref)
201{
202 struct rpmsg_endpoint *ept = container_of(kref, struct rpmsg_endpoint,
203 refcount);
204 /*
205 * At this point no one holds a reference to ept anymore,
206 * so we can directly free it
207 */
208 kfree(ept);
209}
210
191/* for more info, see below documentation of rpmsg_create_ept() */ 211/* for more info, see below documentation of rpmsg_create_ept() */
192static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, 212static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
193 struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb, 213 struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb,
@@ -206,6 +226,8 @@ static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
206 return NULL; 226 return NULL;
207 } 227 }
208 228
229 kref_init(&ept->refcount);
230
209 ept->rpdev = rpdev; 231 ept->rpdev = rpdev;
210 ept->cb = cb; 232 ept->cb = cb;
211 ept->priv = priv; 233 ept->priv = priv;
@@ -238,7 +260,7 @@ rem_idr:
238 idr_remove(&vrp->endpoints, request); 260 idr_remove(&vrp->endpoints, request);
239free_ept: 261free_ept:
240 mutex_unlock(&vrp->endpoints_lock); 262 mutex_unlock(&vrp->endpoints_lock);
241 kfree(ept); 263 kref_put(&ept->refcount, __ept_release);
242 return NULL; 264 return NULL;
243} 265}
244 266
@@ -306,7 +328,7 @@ __rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept)
306 idr_remove(&vrp->endpoints, ept->addr); 328 idr_remove(&vrp->endpoints, ept->addr);
307 mutex_unlock(&vrp->endpoints_lock); 329 mutex_unlock(&vrp->endpoints_lock);
308 330
309 kfree(ept); 331 kref_put(&ept->refcount, __ept_release);
310} 332}
311 333
312/** 334/**
@@ -790,7 +812,13 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
790 812
791 /* use the dst addr to fetch the callback of the appropriate user */ 813 /* use the dst addr to fetch the callback of the appropriate user */
792 mutex_lock(&vrp->endpoints_lock); 814 mutex_lock(&vrp->endpoints_lock);
815
793 ept = idr_find(&vrp->endpoints, msg->dst); 816 ept = idr_find(&vrp->endpoints, msg->dst);
817
818 /* let's make sure no one deallocates ept while we use it */
819 if (ept)
820 kref_get(&ept->refcount);
821
794 mutex_unlock(&vrp->endpoints_lock); 822 mutex_unlock(&vrp->endpoints_lock);
795 823
796 if (ept && ept->cb) 824 if (ept && ept->cb)
@@ -798,6 +826,10 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
798 else 826 else
799 dev_warn(dev, "msg received with no recepient\n"); 827 dev_warn(dev, "msg received with no recepient\n");
800 828
829 /* farewell, ept, we don't need you anymore */
830 if (ept)
831 kref_put(&ept->refcount, __ept_release);
832
801 /* publish the real size of the buffer */ 833 /* publish the real size of the buffer */
802 sg_init_one(&sg, msg, RPMSG_BUF_SIZE); 834 sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
803 835