123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include "hab.h"
- struct virtual_channel *
- hab_vchan_alloc(struct uhab_context *ctx, struct physical_channel *pchan,
- int openid)
- {
- int id;
- struct virtual_channel *vchan;
- if (!pchan || !ctx)
- return NULL;
- vchan = kzalloc(sizeof(*vchan), GFP_KERNEL);
- if (!vchan)
- return NULL;
- /* This should be the first thing we do in this function */
- idr_preload(GFP_KERNEL);
- spin_lock_bh(&pchan->vid_lock);
- id = idr_alloc(&pchan->vchan_idr, vchan, 1,
- (HAB_VCID_ID_MASK >> HAB_VCID_ID_SHIFT) + 1, GFP_NOWAIT);
- spin_unlock_bh(&pchan->vid_lock);
- idr_preload_end();
- if (id <= 0) {
- pr_err("idr failed %d\n", id);
- kfree(vchan);
- return NULL;
- }
- mb(); /* id must be generated done before pchan_get */
- hab_pchan_get(pchan);
- vchan->pchan = pchan;
- /* vchan need both vcid and openid to be properly located */
- vchan->session_id = openid;
- write_lock(&pchan->vchans_lock);
- list_add_tail(&vchan->pnode, &pchan->vchannels);
- pchan->vcnt++;
- write_unlock(&pchan->vchans_lock);
- vchan->id = ((id << HAB_VCID_ID_SHIFT) & HAB_VCID_ID_MASK) |
- ((pchan->habdev->id << HAB_VCID_MMID_SHIFT) &
- HAB_VCID_MMID_MASK) |
- ((pchan->dom_id << HAB_VCID_DOMID_SHIFT) &
- HAB_VCID_DOMID_MASK);
- spin_lock_init(&vchan->rx_lock);
- INIT_LIST_HEAD(&vchan->rx_list);
- init_waitqueue_head(&vchan->rx_queue);
- kref_init(&vchan->refcount);
- vchan->otherend_closed = pchan->closed;
- hab_ctx_get(ctx);
- vchan->ctx = ctx;
- return vchan;
- }
- static void
- hab_vchan_free(struct kref *ref)
- {
- struct virtual_channel *vchan =
- container_of(ref, struct virtual_channel, refcount);
- struct hab_message *message, *msg_tmp;
- struct physical_channel *pchan = vchan->pchan;
- struct uhab_context *ctx = vchan->ctx;
- struct virtual_channel *vc, *vc_tmp;
- int irqs_disabled = irqs_disabled();
- hab_spin_lock(&vchan->rx_lock, irqs_disabled);
- list_for_each_entry_safe(message, msg_tmp, &vchan->rx_list, node) {
- list_del(&message->node);
- hab_msg_free(message);
- }
- hab_spin_unlock(&vchan->rx_lock, irqs_disabled);
- /* release vchan from pchan. no more msg for this vchan */
- hab_write_lock(&pchan->vchans_lock, irqs_disabled);
- list_for_each_entry_safe(vc, vc_tmp, &pchan->vchannels, pnode) {
- if (vchan == vc) {
- list_del(&vc->pnode);
- /* the ref is held in case of pchan is freed */
- pchan->vcnt--;
- break;
- }
- }
- hab_write_unlock(&pchan->vchans_lock, irqs_disabled);
- /* 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 */
- hab_spin_lock(&pchan->vid_lock, irqs_disabled);
- idr_remove(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan->id));
- hab_spin_unlock(&pchan->vid_lock, irqs_disabled);
- hab_pchan_put(pchan); /* no more need for pchan from this vchan */
- kfree(vchan);
- }
- /*
- * only for msg recv path to retrieve vchan from vcid and openid based on
- * pchan's vchan list
- */
- struct virtual_channel*
- hab_vchan_get(struct physical_channel *pchan, struct hab_header *header)
- {
- struct virtual_channel *vchan;
- uint32_t vchan_id = HAB_HEADER_GET_ID(*header);
- uint32_t session_id = HAB_HEADER_GET_SESSION_ID(*header);
- size_t sizebytes = HAB_HEADER_GET_SIZE(*header);
- uint32_t payload_type = HAB_HEADER_GET_TYPE(*header);
- int irqs_disabled = irqs_disabled();
- hab_spin_lock(&pchan->vid_lock, irqs_disabled);
- vchan = idr_find(&pchan->vchan_idr, HAB_VCID_GET_ID(vchan_id));
- if (vchan) {
- if (vchan->session_id != session_id)
- /*
- * skipped if session is different even vcid
- * is the same
- */
- vchan = NULL;
- else if (!vchan->otherend_id /*&& !vchan->session_id*/) {
- /*
- * not paired vchan can be fetched right after it is
- * alloc'ed. so it has to be skipped during search
- * for remote msg
- */
- pr_warn("vcid %x is not paired yet session %d refcnt %d type %d sz %zd\n",
- vchan->id, vchan->otherend_id,
- get_refcnt(vchan->refcount),
- payload_type, sizebytes);
- vchan = NULL;
- } else if (vchan->otherend_closed || vchan->closed) {
- pr_debug("closed already remote %d local %d vcid %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
- vchan->otherend_closed, vchan->closed,
- vchan->id, vchan->otherend_id,
- vchan->session_id, get_refcnt(vchan->refcount),
- vchan_id, session_id, payload_type, sizebytes);
- vchan = NULL;
- } else if (!kref_get_unless_zero(&vchan->refcount)) {
- /*
- * this happens when refcnt is already zero
- * (put from other thread) or there is an actual error
- */
- pr_err("failed to inc vcid %pK %x remote %x session %d refcnt %d header %x session %d type %d sz %zd\n",
- vchan, vchan->id, vchan->otherend_id,
- vchan->session_id, get_refcnt(vchan->refcount),
- vchan_id, session_id, payload_type, sizebytes);
- vchan = NULL;
- }
- }
- hab_spin_unlock(&pchan->vid_lock, irqs_disabled);
- return vchan;
- }
- /* wake up local waiting Q, so stop-vchan can be processed */
- void hab_vchan_stop(struct virtual_channel *vchan)
- {
- if (vchan) {
- vchan->otherend_closed = 1;
- wake_up(&vchan->rx_queue);
- if (vchan->ctx)
- if (vchan->pchan->mem_proto == 1)
- wake_up_interruptible(&vchan->ctx->imp_wq);
- else
- wake_up_interruptible(&vchan->ctx->exp_wq);
- else
- pr_err("NULL ctx for vchan %x\n", vchan->id);
- }
- }
- void hab_vchans_stop(struct physical_channel *pchan)
- {
- struct virtual_channel *vchan, *tmp;
- read_lock(&pchan->vchans_lock);
- list_for_each_entry_safe(vchan, tmp, &pchan->vchannels, pnode) {
- hab_vchan_stop(vchan);
- }
- read_unlock(&pchan->vchans_lock);
- }
- /* send vchan close to remote and stop receiving anything locally */
- void hab_vchan_stop_notify(struct virtual_channel *vchan)
- {
- hab_send_close_msg(vchan);
- hab_vchan_stop(vchan);
- }
- static int hab_vchans_per_pchan_empty(struct physical_channel *pchan)
- {
- int empty;
- read_lock(&pchan->vchans_lock);
- empty = list_empty(&pchan->vchannels);
- if (!empty) {
- struct virtual_channel *vchan;
- int vcnt = pchan->vcnt;
- list_for_each_entry(vchan, &pchan->vchannels, pnode) {
- /* discount open-pending unpaired vchan */
- if (!vchan->session_id)
- vcnt--;
- else
- pr_err("vchan %pK %x rm %x sn %d rf %d clsd %d rm clsd %d\n",
- vchan, vchan->id,
- vchan->otherend_id,
- vchan->session_id,
- get_refcnt(vchan->refcount),
- vchan->closed, vchan->otherend_closed);
- }
- if (!vcnt)
- empty = 1;/* unpaired vchan can exist at init time */
- }
- read_unlock(&pchan->vchans_lock);
- return empty;
- }
- static int hab_vchans_empty(int vmid)
- {
- int i, empty = 1;
- struct physical_channel *pchan;
- struct hab_device *hab_dev;
- for (i = 0; i < hab_driver.ndevices; i++) {
- hab_dev = &hab_driver.devp[i];
- read_lock_bh(&hab_dev->pchan_lock);
- list_for_each_entry(pchan, &hab_dev->pchannels, node) {
- if (pchan->vmid_remote == vmid) {
- if (!hab_vchans_per_pchan_empty(pchan)) {
- empty = 0;
- pr_info("vmid %d %s's vchans are not closed\n",
- vmid, pchan->name);
- break;
- }
- }
- }
- read_unlock_bh(&hab_dev->pchan_lock);
- }
- return empty;
- }
- /*
- * block until all vchans of a given GVM are explicitly closed
- * with habmm_socket_close() by hab clients themselves
- */
- void hab_vchans_empty_wait(int vmid)
- {
- pr_info("waiting for GVM%d's sockets closure\n", vmid);
- while (!hab_vchans_empty(vmid))
- usleep_range(10000, 12000);
- pr_info("all of GVM%d's sockets are closed\n", vmid);
- }
- int hab_vchan_find_domid(struct virtual_channel *vchan)
- {
- return vchan ? vchan->pchan->dom_id : -1;
- }
- void hab_vchan_put(struct virtual_channel *vchan)
- {
- if (vchan)
- kref_put(&vchan->refcount, hab_vchan_free);
- }
- int hab_vchan_query(struct uhab_context *ctx, int32_t vcid, uint64_t *ids,
- char *names, size_t name_size, uint32_t flags)
- {
- struct virtual_channel *vchan;
- vchan = hab_get_vchan_fromvcid(vcid, ctx, 1);
- if (!vchan)
- return -EINVAL;
- if (vchan->otherend_closed) {
- hab_vchan_put(vchan);
- return -ENODEV;
- }
- *ids = vchan->pchan->vmid_local |
- ((uint64_t)vchan->pchan->vmid_remote) << 32;
- names[0] = 0;
- names[name_size/2] = 0;
- hab_vchan_put(vchan);
- return 0;
- }
|