diff --git a/xen/common/argo.c b/xen/common/argo.c index ec113a8a8c97..16c3a3e16549 100644 --- a/xen/common/argo.c +++ b/xen/common/argo.c @@ -43,6 +43,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_argo_addr_t); DEFINE_XEN_GUEST_HANDLE(xen_argo_gfn_t); DEFINE_XEN_GUEST_HANDLE(xen_argo_register_ring_t); DEFINE_XEN_GUEST_HANDLE(xen_argo_ring_t); +DEFINE_XEN_GUEST_HANDLE(xen_argo_unregister_ring_t); static bool __read_mostly opt_argo; static bool __read_mostly opt_argo_mac_permissive; @@ -346,6 +347,33 @@ find_ring_info(const struct domain *d, const struct argo_ring_id *id) return NULL; } +static struct argo_send_info * +find_send_info(const struct domain *d, const struct argo_ring_id *id) +{ + struct hlist_node *node; + struct argo_send_info *send_info; + + ASSERT(LOCKING_send_L2(d)); + + hlist_for_each_entry(send_info, node, &d->argo->send_hash[hash_index(id)], + node) + { + const struct argo_ring_id *cmpid = &send_info->id; + + if ( cmpid->aport == id->aport && + cmpid->domain_id == id->domain_id && + cmpid->partner_id == id->partner_id ) + { + argo_dprintk("send_info=%p\n", send_info); + return send_info; + } + } + argo_dprintk("no send_info found for ring(%u:%x %u)\n", + id->domain_id, id->aport, id->partner_id); + + return NULL; +} + static void ring_unmap(const struct domain *d, struct argo_ring_info *ring_info) { @@ -713,6 +741,81 @@ find_ring_mfns(struct domain *d, struct argo_ring_info *ring_info, * * shrink critical sections: move acquire/release of the global lock. * * simplify the out label path when lock release has been moved. */ +static long +unregister_ring(struct domain *currd, + XEN_GUEST_HANDLE_PARAM(xen_argo_unregister_ring_t) unreg_hnd) +{ + xen_argo_unregister_ring_t unreg; + struct argo_ring_id ring_id; + struct argo_ring_info *ring_info; + struct argo_send_info *send_info = NULL; + struct domain *dst_d = NULL; + int ret = 0; + + ASSERT(currd == current->domain); + + if ( copy_from_guest(&unreg, unreg_hnd, 1) ) + return -EFAULT; + + if ( unreg.pad ) + return -EINVAL; + + ring_id.partner_id = unreg.partner_id; + ring_id.aport = unreg.aport; + ring_id.domain_id = currd->domain_id; + + read_lock(&L1_global_argo_rwlock); + + if ( !currd->argo ) + { + ret = -ENODEV; + goto out_unlock; + } + + write_lock(&currd->argo->rings_L2_rwlock); + + ring_info = find_ring_info(currd, &ring_id); + if ( ring_info ) + { + ring_remove_info(currd, ring_info); + currd->argo->ring_count--; + } + + dst_d = get_domain_by_id(ring_id.partner_id); + if ( dst_d ) + { + if ( dst_d->argo ) + { + spin_lock(&dst_d->argo->send_L2_lock); + + send_info = find_send_info(dst_d, &ring_id); + if ( send_info ) + hlist_del(&send_info->node); + + spin_unlock(&dst_d->argo->send_L2_lock); + + } + put_domain(dst_d); + } + + write_unlock(&currd->argo->rings_L2_rwlock); + + if ( send_info ) + xfree(send_info); + + if ( !ring_info ) + { + argo_dprintk("ENOENT\n"); + ret = -ENOENT; + goto out_unlock; + } + + out_unlock: + read_unlock(&L1_global_argo_rwlock); + + return ret; +} + static long register_ring(struct domain *currd, XEN_GUEST_HANDLE_PARAM(xen_argo_register_ring_t) reg_hnd, @@ -999,6 +1102,21 @@ do_argo_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg1, break; } + case XEN_ARGO_OP_unregister_ring: + { + XEN_GUEST_HANDLE_PARAM(xen_argo_unregister_ring_t) unreg_hnd = + guest_handle_cast(arg1, xen_argo_unregister_ring_t); + + if ( unlikely((!guest_handle_is_null(arg2)) || arg3 || arg4) ) + { + rc = -EINVAL; + break; + } + + rc = unregister_ring(currd, unreg_hnd); + break; + } + default: rc = -EOPNOTSUPP; break; diff --git a/xen/common/compat/argo.c b/xen/common/compat/argo.c index 9437a7aa892f..6a1671c2cf5c 100644 --- a/xen/common/compat/argo.c +++ b/xen/common/compat/argo.c @@ -22,3 +22,4 @@ CHECK_argo_addr; CHECK_argo_register_ring; CHECK_argo_ring; +CHECK_argo_unregister_ring; diff --git a/xen/include/public/argo.h b/xen/include/public/argo.h index bd2337309aad..3eabf836d71e 100644 --- a/xen/include/public/argo.h +++ b/xen/include/public/argo.h @@ -79,6 +79,13 @@ typedef struct xen_argo_register_ring uint32_t len; } xen_argo_register_ring_t; +typedef struct xen_argo_unregister_ring +{ + xen_argo_port_t aport; + domid_t partner_id; + uint16_t pad; +} xen_argo_unregister_ring_t; + /* Messages on the ring are padded to a multiple of this size. */ #define XEN_ARGO_MSG_SLOT_SIZE 0x10 @@ -138,4 +145,16 @@ struct xen_argo_ring_message_header /* Mask for all defined flags. unsigned long type so ok for both 32/64-bit */ #define XEN_ARGO_REGISTER_FLAG_MASK 0x1UL +/* + * XEN_ARGO_OP_unregister_ring + * + * Unregister a previously-registered ring, ending communication. + * + * arg1: XEN_GUEST_HANDLE(xen_argo_unregister_ring_t) + * arg2: NULL + * arg3: 0 (ZERO) + * arg4: 0 (ZERO) + */ +#define XEN_ARGO_OP_unregister_ring 2 + #endif diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst index 9c9d33fac32a..411c66107c95 100644 --- a/xen/include/xlat.lst +++ b/xen/include/xlat.lst @@ -151,3 +151,4 @@ ? argo_addr argo.h ? argo_ring argo.h ? argo_register_ring argo.h +? argo_unregister_ring argo.h