From cf41f3e0d3c2e5025f401a61cfaa7a79d27cc586 Mon Sep 17 00:00:00 2001 From: Yong Ding Date: Thu, 1 Nov 2018 15:59:49 +0800 Subject: [PATCH] soc: qcom: hab: refine hab virtual channel's resource free 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 --- drivers/soc/qcom/hab/hab.c | 33 +++++++++++++-------------------- drivers/soc/qcom/hab/hab_vchan.c | 40 ++++------------------------------------ 2 files changed, 17 insertions(+), 56 deletions(-) diff --git a/drivers/soc/qcom/hab/hab.c b/drivers/soc/qcom/hab/hab.c index 7d53e6162172..24461d2c957b 100644 --- a/drivers/soc/qcom/hab/hab.c +++ b/drivers/soc/qcom/hab/hab.c @@ -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); } diff --git a/drivers/soc/qcom/hab/hab_vchan.c b/drivers/soc/qcom/hab/hab_vchan.c index 770c6ba3dadd..a95d6c7451c9 100644 --- a/drivers/soc/qcom/hab/hab_vchan.c +++ b/drivers/soc/qcom/hab/hab_vchan.c @@ -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, -- 2.11.0