Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf41f3e0 authored by Yong Ding's avatar Yong Ding
Browse files

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: default avatarYong Ding <yongding@codeaurora.org>
parent 67b38b04
Loading
Loading
Loading
Loading
+13 −20
Original line number Diff line number Diff line
@@ -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) {
		/* local close starts */
		vchan->closed = 1;

		list_del(&vchan->node); /* vchan is not in this ctx anymore */
		ctx->vcnt--;

		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;

		/* for the missing local vchan close */
		write_unlock(&ctx->ctx_lock);
		hab_vchan_put(vchan); /* there is a lock inside */
		write_lock(&ctx->ctx_lock);
	}
+4 −36
Original line number Diff line number Diff line
@@ -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,