OSDN Git Service

soc: qcom: hab: refine hab virtual channel's resource free
authorYong Ding <yongding@codeaurora.org>
Thu, 1 Nov 2018 07:59:49 +0000 (15:59 +0800)
committerYong Ding <yongding@codeaurora.org>
Thu, 1 Nov 2018 09:37:29 +0000 (17:37 +0800)
Whenever a vchan is locally closed in hab_vchan_close or hab_free,
4 actions should be taken immediately, including,
 - remove it from the local hab context
 - mark its local closed flag
 - notify remote side and unblock local blocking calls over it
 - decrease the refcnt on the vchan

Change-Id: I3fbde9464f6405b6dadac248768a5fd857a29128
Signed-off-by: Yong Ding <yongding@codeaurora.org>
drivers/soc/qcom/hab/hab.c
drivers/soc/qcom/hab/hab_vchan.c

index 7d53e61..24461d2 100644 (file)
@@ -720,11 +720,18 @@ void hab_vchan_close(struct uhab_context *ctx, int32_t vcid)
        write_lock(&ctx->ctx_lock);
        list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
                if (vchan->id == vcid) {
+                       /* local close starts */
                        vchan->closed = 1;
-                       write_unlock(&ctx->ctx_lock);
+
+                       /* vchan is not in this ctx anymore */
+                       list_del(&vchan->node);
+                       ctx->vcnt--;
+
                        pr_debug("vcid %x remote %x session %d refcnt %d\n",
                                vchan->id, vchan->otherend_id,
                                vchan->session_id, get_refcnt(vchan->refcount));
+
+                       write_unlock(&ctx->ctx_lock);
                        /* unblocking blocked in-calls */
                        hab_vchan_stop_notify(vchan);
                        hab_vchan_put(vchan); /* there is a lock inside */
@@ -1069,28 +1076,14 @@ static int hab_release(struct inode *inodep, struct file *filep)
        write_lock(&ctx->ctx_lock);
        /* notify remote side on vchan closing */
        list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
-               list_del(&vchan->node); /* vchan is not in this ctx anymore */
+               /* local close starts */
+               vchan->closed = 1;
 
-               if (!vchan->closed) { /* locally hasn't closed yet */
-                       if (!kref_get_unless_zero(&vchan->refcount)) {
-                               pr_err("vchan %x %x refcnt %d mismanaged closed %d remote closed %d\n",
-                                       vchan->id,
-                                       vchan->otherend_id,
-                                       get_refcnt(vchan->refcount),
-                                       vchan->closed, vchan->otherend_closed);
-                               continue; /* vchan is already being freed */
-                       } else {
-                               write_unlock(&ctx->ctx_lock);
-                               hab_vchan_stop_notify(vchan);
-                               /* put for notify. shouldn't cause free */
-                               hab_vchan_put(vchan);
-                               write_lock(&ctx->ctx_lock);
-                       }
-               } else
-                       continue;
+               list_del(&vchan->node); /* vchan is not in this ctx anymore */
+               ctx->vcnt--;
 
-               /* for the missing local vchan close */
                write_unlock(&ctx->ctx_lock);
+               hab_vchan_stop_notify(vchan);
                hab_vchan_put(vchan); /* there is a lock inside */
                write_lock(&ctx->ctx_lock);
        }
index 770c6ba..a95d6c7 100644 (file)
@@ -85,9 +85,6 @@ hab_vchan_free(struct kref *ref)
        }
        spin_unlock_bh(&vchan->rx_lock);
 
-       /* the release vchan from ctx was done earlier in vchan close() */
-       hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */
-
        /* release vchan from pchan. no more msg for this vchan */
        write_lock_bh(&pchan->vchans_lock);
        list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
@@ -100,6 +97,9 @@ hab_vchan_free(struct kref *ref)
        }
        write_unlock_bh(&pchan->vchans_lock);
 
+       /* the release vchan from ctx was done earlier in vchan close() */
+       hab_ctx_put(ctx); /* now ctx is not needed from this vchan's view */
+
        /* release idr at the last so same idr will not be used early */
        spin_lock_bh(&pchan->vid_lock);
        idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
@@ -274,42 +274,10 @@ int hab_vchan_find_domid(struct virtual_channel *vchan)
        return vchan ? vchan->pchan->dom_id : -1;
 }
 
-/* this sould be only called once after refcnt is zero */
-static void hab_vchan_schedule_free(struct kref *ref)
-{
-       struct virtual_channel *vchanin =
-               container_of(ref, struct virtual_channel, refcount);
-       struct uhab_context *ctx = vchanin->ctx;
-       struct virtual_channel *vchan, *tmp;
-       int bnotify = 0;
-
-       /*
-        * similar logic is in ctx free. if ctx free runs first,
-        * this is skipped
-        */
-       write_lock_bh(&ctx->ctx_lock);
-       list_for_each_entry_safe(vchan, tmp, &ctx->vchannels, node) {
-               if (vchan == vchanin) {
-                       pr_debug("vchan free refcnt = %d\n",
-                                        get_refcnt(vchan->refcount));
-                       ctx->vcnt--;
-                       list_del(&vchan->node);
-                       bnotify = 1;
-                       break;
-               }
-       }
-       write_unlock_bh(&ctx->ctx_lock);
-
-       if (bnotify)
-               hab_vchan_stop_notify(vchan);
-
-       hab_vchan_free(ref);
-}
-
 void hab_vchan_put(struct virtual_channel *vchan)
 {
        if (vchan)
-               kref_put(&vchan->refcount, hab_vchan_schedule_free);
+               kref_put(&vchan->refcount, hab_vchan_free);
 }
 
 int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,