]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/rpmsg/qcom_glink_native.c
Merge tag 'for-linus-5.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
[linux.git] / drivers / rpmsg / qcom_glink_native.c
index 621f1afd4d6b159c761700d2a3a2c4380a169260..1995f5b3ea6775e22f85dc97461a107fc5b4c561 100644 (file)
@@ -241,10 +241,31 @@ static void qcom_glink_channel_release(struct kref *ref)
 {
        struct glink_channel *channel = container_of(ref, struct glink_channel,
                                                     refcount);
+       struct glink_core_rx_intent *intent;
+       struct glink_core_rx_intent *tmp;
        unsigned long flags;
+       int iid;
+
+       /* cancel pending rx_done work */
+       cancel_work_sync(&channel->intent_work);
 
        spin_lock_irqsave(&channel->intent_lock, flags);
+       /* Free all non-reuse intents pending rx_done work */
+       list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) {
+               if (!intent->reuse) {
+                       kfree(intent->data);
+                       kfree(intent);
+               }
+       }
+
+       idr_for_each_entry(&channel->liids, tmp, iid) {
+               kfree(tmp->data);
+               kfree(tmp);
+       }
        idr_destroy(&channel->liids);
+
+       idr_for_each_entry(&channel->riids, tmp, iid)
+               kfree(tmp);
        idr_destroy(&channel->riids);
        spin_unlock_irqrestore(&channel->intent_lock, flags);
 
@@ -1094,13 +1115,12 @@ static int qcom_glink_create_remote(struct qcom_glink *glink,
 close_link:
        /*
         * Send a close request to "undo" our open-ack. The close-ack will
-        * release the last reference.
+        * release qcom_glink_send_open_req() reference and the last reference
+        * will be relesed after receiving remote_close or transport unregister
+        * by calling qcom_glink_native_remove().
         */
        qcom_glink_send_close_req(glink, channel);
 
-       /* Release qcom_glink_send_open_req() reference */
-       kref_put(&channel->refcount, qcom_glink_channel_release);
-
        return ret;
 }
 
@@ -1415,15 +1435,13 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid,
 
                ret = rpmsg_register_device(rpdev);
                if (ret)
-                       goto free_rpdev;
+                       goto rcid_remove;
 
                channel->rpdev = rpdev;
        }
 
        return 0;
 
-free_rpdev:
-       kfree(rpdev);
 rcid_remove:
        spin_lock_irqsave(&glink->idr_lock, flags);
        idr_remove(&glink->rcids, channel->rcid);
@@ -1544,6 +1562,18 @@ static void qcom_glink_work(struct work_struct *work)
        }
 }
 
+static void qcom_glink_cancel_rx_work(struct qcom_glink *glink)
+{
+       struct glink_defer_cmd *dcmd;
+       struct glink_defer_cmd *tmp;
+
+       /* cancel any pending deferred rx_work */
+       cancel_work_sync(&glink->rx_work);
+
+       list_for_each_entry_safe(dcmd, tmp, &glink->rx_queue, node)
+               kfree(dcmd);
+}
+
 struct qcom_glink *qcom_glink_native_probe(struct device *dev,
                                           unsigned long features,
                                           struct qcom_glink_pipe *rx,
@@ -1619,23 +1649,24 @@ void qcom_glink_native_remove(struct qcom_glink *glink)
        struct glink_channel *channel;
        int cid;
        int ret;
-       unsigned long flags;
 
        disable_irq(glink->irq);
-       cancel_work_sync(&glink->rx_work);
+       qcom_glink_cancel_rx_work(glink);
 
        ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device);
        if (ret)
                dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret);
 
-       spin_lock_irqsave(&glink->idr_lock, flags);
        /* Release any defunct local channels, waiting for close-ack */
        idr_for_each_entry(&glink->lcids, channel, cid)
                kref_put(&channel->refcount, qcom_glink_channel_release);
 
+       /* Release any defunct local channels, waiting for close-req */
+       idr_for_each_entry(&glink->rcids, channel, cid)
+               kref_put(&channel->refcount, qcom_glink_channel_release);
+
        idr_destroy(&glink->lcids);
        idr_destroy(&glink->rcids);
-       spin_unlock_irqrestore(&glink->idr_lock, flags);
        mbox_free_channel(glink->mbox_chan);
 }
 EXPORT_SYMBOL_GPL(qcom_glink_native_remove);