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 */
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);
}
}
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) {
}
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));
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,