[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v8 for-4.12 04/17] argo: init, destroy and soft-reset, with enable command line opt



Initialises basic data structures and performs teardown of argo state
for domain shutdown.

Inclusion of the Argo implementation is dependent on CONFIG_ARGO.

Introduces a new Xen command line parameter 'argo': bool to enable/disable
the argo hypercall. Defaults to disabled.

New headers:
  public/argo.h: with definions of addresses and ring structure, including
  indexes for atomic update for communication between domain and hypervisor.

  xen/argo.h: to expose the hooks for integration into domain lifecycle:
    argo_init: per-domain init of argo data structures for domain_create.
    argo_destroy: teardown for domain_destroy and the error exit
                  path of domain_create.
    argo_soft_reset: reset of domain state for domain_soft_reset.

Adds a new field to struct domain: struct argo_domain *argo;

In accordance with recent work on _domain_destroy, argo_destroy is
idempotent. It will tear down: all rings registered by this domain, all
rings where this domain is the single sender (ie. specified partner,
non-wildcard rings), and all pending notifications where this domain is
awaiting signal about available space in the rings of other domains.

A count will be maintained of the number of rings that a domain has
registered in order to limit it below the fixed maximum limit defined here.

Macros are defined to verify the internal locking state within the argo
implementation. The macros are ASSERTed on entry to functions to validate
and document the required lock state prior to calling.

The hash function for the hashtables that hold ring state is derived from
the string hashing function djb2 (http://www.cse.yorku.ca/~oz/hash.html)
by Daniel J. Bernstein. Basic testing with a limited number of domains and
ports has shown reasonable distribution for the table size.

The software license on the public header is the BSD license, standard
procedure for the public Xen headers. The public header was originally
posted under a GPL license at: [1]:
https://lists.xenproject.org/archives/html/xen-devel/2013-05/msg02710.html

The following ACK by Lars Kurth is to confirm that only people being
employees of Citrix contributed to the header files in the series posted at
[1] and that thus the copyright of the files in question is fully owned by
Citrix. The ACK also confirms that Citrix is happy for the header files to
be published under a BSD license in this series (which is based on [1]).

Signed-off-by: Christopher Clark <christopher.clark6@xxxxxxxxxxxxxx>
Acked-by: Lars Kurth <lars.kurth@xxxxxxxxxx>
Reviewed-by: Ross Philipson <ross.philipson@xxxxxxxxxx>
Tested-by: Chris Patterson <pattersonc@xxxxxxxxxxxx>
Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
---
v7 #02 Julien/Jan/Stefano: apply range check to numeric hypercall args
v7 #10 Jan: avoid code duplication: forward compat ops to native
v7 #04 Roger: add Reviewed-by
v7 #04 Roger: list_first_entry_or_null in partner_rings_remove
v7 #04 Roger: list_first_entry_or_null in domain_rings_remove_all
v7 #04 Roger: list_first_entry_or_null in wildcard_rings_pending_remove
v7 #04 Roger: list_first_entry_or_null in pending_remove_all
v7 #04 Roger: add space to compat switch ( cmd )
v7 #04 Roger: add space to switch ( cmd )
v7 #07 Roger: remove stray newline in compat_argo_op
v6 #09 Jan: introduce compat ABI
v6 #04 Jan: xlat.lst: move argo struct entries to alphabetical position
v6 #04 Roger: use list_for_each_entry{_safe} for looping
v5 #04 Roger: tweak command line doc: remove statement about top level bool
v5: add compat validation macros to primary source file: common/argo.c
v5: dropped external file for compat macros: common/compat/argo.c
v4: removed FIXME for removing argo_destroy from domain_kill
v4 Jan: amend the command line doc text referring to build configuration
v4 : use standard data structures as per common code
v4 Jan: replace hash_index with djb2-derived hash algorithm
v4 Andrew: switch argo command line option to list argo=<bool>
v4 #04 Roger: drop unneeded init of ring_count in argo_domain_init
v4 #04 Roger: replace if (ring_info->mfns) with ASSERTs in ring_unmap
v4 #04 Roger: rewrite the locking verification macros
v4 #04 Roger: make L1 lock description comment clearer about R(L1) and W(L1)
v4 Andrew: fix split of dprintk in ring_map_info across v4 commits
v3 #04 Andrew: use xzalloc for struct argo_domain in argo_init
v3 #04 Andrew: reference CONFIG_ARGO in the command line documentation
v3 #07 Jan: rename ring_find_info to find_ring_info
v3 #04 Andrew: don't truncate args do_argo_op printk
v3 #07 Jan: fix numeric entries in printk format strings
v3 #10 Roger: move find functions to top of file and drop prototypes
v3 #04 Jan: meld compat check for hypercall arg types
v3 #04 Roger/Jan: make lock names clearer and assert their state
v3 #04 Jan: port -> aport with type; distinguish argo port from evtchn
v3 #04 Jan: reorder call to argo_init_domain in argo_init
v3 #04 Jan: ring_remove_mfns: zero count before freeing arrays
v3 #04 Jason/Roger: soft_reset: can assume reinit is ok if d->argo set
v3 #04 Roger: remove unused and confusing d->argo_lock
v3 #04 Roger: add simple inlines in xen/argo.h, drop ifdef CONFIG_ARGO
v3 #04 Roger: simpler return -EOPNOTSUPP in do_argo_op
v3 #04 Roger: add const to domain arg to ring_remove_info
v3 #04 Roger: use XFREE
v3 #04 Roger: newline fix in wildcard_pending_list_remove
v3 #04 Roger: mfn_mapping: void* instead of uint8_t*
v3 #04 Roger: drop npages struct member in argo_ring_info; use len
v3 #04 Roger/Jan: drop many fixed width types in internal structs
v3 #04 Jason/Jan: drop pad and fixed width type in pending_ent struct
v3 #04 Eric: moved ring_find_info from register op into this commit
v3 moved hash_index function, nospec include from register op to this commit
v3 moved XEN_ARGO_DOMID_ANY defn from register op into this commit
v3 added #include <xen/sched.h> to <xen/argo.h> for domain struct defn
v3 feedback #04 Roger: reorder #includes to alphabetical order
v3 Added Ross's Reviewed-by.
v2 rewrite locking explanation comment
v2 header copyright line now includes 2019
v2 self: use ring_info backpointer in pending_ent to maintain npending
v2 self: rename all_rings_remove_info to domain_rings_remove_all
v2 feedback Jan: drop cookie, implement teardown
v2 self: add npending to track number of pending entries per ring
v2 self: amend comment on locking; drop section comments
v2 cookie_eq: test low bits first and use likely on high bits
v2 self: OVERHAUL
v2 self: s/argo_pending_ent/pending_ent/g
v2 self: drop pending_remove_ent, inline at single call site
v1 feedback Roger, Jan: drop argo prefix on static functions
v2 #4 Lars: add Acked-by and details to commit message.
v2 feedback #9 Jan: document argo boot opt in xen-command-line.markdown
v2 bugfix: xsm use in soft-reset prior to introduction
v2 feedback #9 Jan: drop 'message' from do_argo_message_op
v1 #5 feedback Paul: init/destroy unsigned, brackets and whitespace fixes
v1 #5 feedback Paul: Use mfn_eq for comparing mfns.
v1 #5 feedback Paul: init/destroy : use currd
v1 #6 (#5) feedback Jan: init/destroy: s/ENOSYS/EOPNOTSUPP/
v1 #6 feedback Paul: Folded patch 6 into patch 5.
v1 #6 feedback Jan: drop opt_argo_enabled initializer
v1 $6 feedback Jan: s/ENOSYS/EOPNOTSUPP/g and drop useless dprintk
v1. #5 feedback Paul: change the license on public header to BSD
- ack from Lars at Citrix.
v1. self, Jan: drop unnecessary xen include from sched.h
v1. self, Jan: drop inclusion of public argo.h in private one
v1. self, Jan: add include of public argo.h to argo.c
v1. self, Jan: drop fwd decl of argo_domain in priv header
v1. Paul/self/Jan: add data structures to xlat.lst and compat/argo.h to Makefile
v1. self: removed allocation of event channel since switching to VIRQ
v1. self: drop types.h include from private argo.h
v1: reorder public argo include position
v1: #13 feedback Jan: public namespace: prefix with xen
v1: self: rename pending ent "id" to "domain_id"
v1: self: add domain_cookie to ent struct
v1. #15 feedback Jan: make cmd unsigned
v1. #15 feedback Jan: make i loop variable unsigned
v1: self: adjust dprintks in init, destroy
v1: #18 feedback Jan: meld max ring count limit
v1: self: use type not struct in public defn, affects compat gen header
v1: feedback #15 Jan: handle upper-halves of hypercall args
v1: add comment explaining the 'magic' field
v1: self + Jan feedback: implement soft reset
v1: feedback #13 Roger: use ASSERT_UNREACHABLE

 docs/misc/xen-command-line.pandoc |  13 +
 xen/common/argo.c                 | 624 +++++++++++++++++++++++++++++++++++++-
 xen/common/domain.c               |   9 +
 xen/include/Makefile              |   1 +
 xen/include/public/argo.h         |  64 ++++
 xen/include/xen/argo.h            |  44 +++
 xen/include/xen/sched.h           |   5 +
 xen/include/xlat.lst              |   2 +
 8 files changed, 760 insertions(+), 2 deletions(-)
 create mode 100644 xen/include/public/argo.h
 create mode 100644 xen/include/xen/argo.h

diff --git a/docs/misc/xen-command-line.pandoc 
b/docs/misc/xen-command-line.pandoc
index 6a33775..605c544 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -182,6 +182,19 @@ Permit Xen to use "Always Running APIC Timer" support on 
compatible hardware
 in combination with cpuidle.  This option is only expected to be useful for
 developers wishing Xen to fall back to older timing methods on newer hardware.
 
+### argo
+    = List of [ <bool> ]
+
+Controls for the Argo hypervisor-mediated interdomain communication service.
+
+The functionality that this option controls is only available when Xen has been
+compiled with the build setting for Argo enabled in the build configuration.
+
+Argo is a interdomain communication mechanism, where Xen acts as the central
+point of authority.  Guests may register memory rings to recieve messages,
+query the status of other domains, and send messages by hypercall, all subject
+to appropriate auditing by Xen.  Argo is disabled by default.
+
 ### asid (x86)
 > `= <boolean>`
 
diff --git a/xen/common/argo.c b/xen/common/argo.c
index ada1aaf..f1626ca 100644
--- a/xen/common/argo.c
+++ b/xen/common/argo.c
@@ -16,8 +16,261 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <xen/argo.h>
+#include <xen/domain.h>
+#include <xen/domain_page.h>
 #include <xen/errno.h>
+#include <xen/event.h>
 #include <xen/guest_access.h>
+#include <xen/nospec.h>
+#include <xen/sched.h>
+#include <xen/time.h>
+
+#include <public/argo.h>
+
+#ifdef CONFIG_COMPAT
+#include <compat/argo.h>
+CHECK_argo_addr;
+CHECK_argo_ring;
+#endif
+
+DEFINE_XEN_GUEST_HANDLE(xen_argo_addr_t);
+DEFINE_XEN_GUEST_HANDLE(xen_argo_ring_t);
+
+static bool __read_mostly opt_argo;
+
+static int __init parse_argo(const char *s)
+{
+    const char *ss;
+    int val, rc = 0;
+
+    do {
+        ss = strchr(s, ',');
+        if ( !ss )
+            ss = strchr(s, '\0');
+
+        if ( (val = parse_bool(s, ss)) >= 0 )
+            opt_argo = val;
+        else
+            rc = -EINVAL;
+
+        s = ss + 1;
+    } while ( *ss );
+
+    return rc;
+}
+custom_param("argo", parse_argo);
+
+typedef struct argo_ring_id
+{
+    xen_argo_port_t aport;
+    domid_t partner_id;
+    domid_t domain_id;
+} argo_ring_id;
+
+/* Data about a domain's own ring that it has registered */
+struct argo_ring_info
+{
+    /* next node in the hash, protected by rings_L2 */
+    struct list_head node;
+    /* this ring's id, protected by rings_L2 */
+    struct argo_ring_id id;
+    /* L3, the ring_info lock: protects the members of this struct below */
+    spinlock_t L3_lock;
+    /* length of the ring, protected by L3 */
+    unsigned int len;
+    /* number of pages translated into mfns, protected by L3 */
+    unsigned int nmfns;
+    /* cached tx pointer location, protected by L3 */
+    unsigned int tx_ptr;
+    /* mapped ring pages protected by L3 */
+    void **mfn_mapping;
+    /* list of mfns of guest ring, protected by L3 */
+    mfn_t *mfns;
+    /* list of struct pending_ent for this ring, protected by L3 */
+    struct list_head pending;
+    /* number of pending entries queued for this ring, protected by L3 */
+    unsigned int npending;
+};
+
+/* Data about a single-sender ring, held by the sender (partner) domain */
+struct argo_send_info
+{
+    /* next node in the hash, protected by send_L2 */
+    struct list_head node;
+    /* this ring's id, protected by send_L2 */
+    struct argo_ring_id id;
+};
+
+/* A space-available notification that is awaiting sufficient space */
+struct pending_ent
+{
+    /* List node within argo_ring_info's pending list */
+    struct list_head node;
+    /*
+     * List node within argo_domain's wildcard_pend_list. Only used if the
+     * ring is one with a wildcard partner (ie. that any domain may send to)
+     * to enable cancelling signals on wildcard rings on domain destroy.
+     */
+    struct list_head wildcard_node;
+    /*
+     * Pointer to the ring_info that this ent pertains to. Used to ensure that
+     * ring_info->npending is decremented when ents for wildcard rings are
+     * cancelled for domain destroy.
+     * Caution: Must hold the correct locks before accessing ring_info via 
this.
+     */
+    struct argo_ring_info *ring_info;
+    /* minimum ring space available that this signal is waiting upon */
+    unsigned int len;
+    /* domain to be notified when space is available */
+    domid_t domain_id;
+};
+
+/*
+ * The value of the argo element in a struct domain is
+ * protected by L1_global_argo_rwlock
+ */
+#define ARGO_HASHTABLE_SIZE 32
+struct argo_domain
+{
+    /* rings_L2 */
+    rwlock_t rings_L2_rwlock;
+    /*
+     * Hash table of argo_ring_info about rings this domain has registered.
+     * Protected by rings_L2.
+     */
+    struct list_head ring_hash[ARGO_HASHTABLE_SIZE];
+    /* Counter of rings registered by this domain. Protected by rings_L2. */
+    unsigned int ring_count;
+
+    /* send_L2 */
+    spinlock_t send_L2_lock;
+    /*
+     * Hash table of argo_send_info about rings other domains have registered
+     * for this domain to send to. Single partner, non-wildcard rings.
+     * Protected by send_L2.
+     */
+    struct list_head send_hash[ARGO_HASHTABLE_SIZE];
+
+    /* wildcard_L2 */
+    spinlock_t wildcard_L2_lock;
+    /*
+     * List of pending space-available signals for this domain about wildcard
+     * rings registered by other domains. Protected by wildcard_L2.
+     */
+    struct list_head wildcard_pend_list;
+};
+
+/*
+ * Locking is organized as follows:
+ *
+ * Terminology: R(<lock>) means taking a read lock on the specified lock;
+ *              W(<lock>) means taking a write lock on it.
+ *
+ * == L1 : The global read/write lock: L1_global_argo_rwlock
+ * Protects the argo elements of all struct domain *d in the system.
+ *
+ * R(L1) does not protect any of the elements of d->argo; it protects their
+ * addresses. W(L1) protects those and more since it implies W on all the lower
+ * level locks - see the notes on those locks below.
+ *
+ * The destruction of an argo-enabled domain, which must have a non-NULL 
d->argo
+ * pointer, will need to free that d->argo pointer, which requires W(L1).
+ * Since holding R(L1) will block acquiring W(L1), it will ensure that
+ * no domains pointers that argo is interested in become invalid while either
+ * W(L1) or R(L1) are held.
+ */
+
+static DEFINE_RWLOCK(L1_global_argo_rwlock); /* L1 */
+
+/*
+ * == rings_L2 : The per-domain ring hash lock: d->argo->rings_L2_rwlock
+ *
+ * Holding a read lock on rings_L2 protects the ring hash table and
+ * the elements in the hash_table d->argo->ring_hash, and
+ * the node and id fields in struct argo_ring_info in the
+ * hash table.
+ * Holding a write lock on rings_L2 protects all of the elements of all the
+ * struct argo_ring_info belonging to this domain.
+ *
+ * To take rings_L2 you must already have R(L1). W(L1) implies W(rings_L2) and
+ * L3.
+ *
+ * == L3 : The individual ring_info lock: ring_info->L3_lock
+ *
+ * Protects all the fields within the argo_ring_info, aside from the ones that
+ * rings_L2 already protects: node, id, lock.
+ *
+ * To acquire L3 you must already have R(rings_L2). W(rings_L2) implies L3.
+ *
+ * == send_L2 : The per-domain single-sender partner rings lock:
+ *              d->argo->send_L2_lock
+ *
+ * Protects the per-domain send hash table : d->argo->send_hash
+ * and the elements in the hash table, and the node and id fields
+ * in struct argo_send_info in the hash table.
+ *
+ * To take send_L2, you must already have R(L1). W(L1) implies send_L2.
+ * Do not attempt to acquire a rings_L2 on any domain after taking and while
+ * holding a send_L2 lock -- acquire the rings_L2 (if one is needed) 
beforehand.
+ *
+ * == wildcard_L2 : The per-domain wildcard pending list lock:
+ *                  d->argo->wildcard_L2_lock
+ *
+ * Protects the per-domain list of outstanding signals for space availability
+ * on wildcard rings.
+ *
+ * To take wildcard_L2, you must already have R(L1). W(L1) implies wildcard_L2.
+ * No other locks are acquired after obtaining wildcard_L2.
+ */
+
+/*
+ * Lock state validations macros
+ *
+ * These macros encode the logic to verify that the locking has adhered to the
+ * locking discipline above.
+ * eg. On entry to logic that requires holding at least R(rings_L2), this:
+ *      ASSERT(LOCKING_Read_rings_L2(d));
+ *
+ * checks that the lock state is sufficient, validating that one of the
+ * following must be true when executed:       R(rings_L2) && R(L1)
+ *                                        or:  W(rings_L2) && R(L1)
+ *                                        or:  W(L1)
+ *
+ * The LOCKING macros defined below here are for use at verification points.
+ */
+#define LOCKING_Write_L1 (rw_is_write_locked(&L1_global_argo_rwlock))
+/*
+ * While LOCKING_Read_L1 will return true even if the lock is write-locked,
+ * that's OK because everywhere that a Read lock is needed with these macros,
+ * holding a Write lock there instead is OK too: we're checking that _at least_
+ * the specified level of locks are held.
+ */
+#define LOCKING_Read_L1 (rw_is_locked(&L1_global_argo_rwlock))
+
+#define LOCKING_Write_rings_L2(d) \
+    ((LOCKING_Read_L1 && rw_is_write_locked(&(d)->argo->rings_L2_rwlock)) || \
+     LOCKING_Write_L1)
+/*
+ * Skip checking LOCKING_Write_rings_L2(d) within this LOCKING_Read_rings_L2
+ * definition because the first clause that is testing R(L1) && R(L2) will also
+ * return true if R(L1) && W(L2) is true, because of the way that rw_is_locked
+ * behaves. This results in a slightly shorter and faster implementation.
+ */
+#define LOCKING_Read_rings_L2(d) \
+    ((LOCKING_Read_L1 && rw_is_locked(&(d)->argo->rings_L2_rwlock)) || \
+     LOCKING_Write_L1)
+/*
+ * Skip checking LOCKING_Write_L1 within this LOCKING_L3 definition because
+ * LOCKING_Write_rings_L2(d) will return true for that condition.
+ */
+#define LOCKING_L3(d, r) \
+    ((LOCKING_Read_L1 && rw_is_locked(&(d)->argo->rings_L2_rwlock) \
+      && spin_is_locked(&(r)->L3_lock)) || LOCKING_Write_rings_L2(d))
+
+#define LOCKING_send_L2(d) \
+    ((LOCKING_Read_L1 && spin_is_locked(&(d)->argo->send_L2_lock)) || \
+     LOCKING_Write_L1)
 
 /* Change this to #define ARGO_DEBUG here to enable more debug messages */
 #undef ARGO_DEBUG
@@ -28,12 +281,289 @@
 #define argo_dprintk(format, ... ) ((void)0)
 #endif
 
+/*
+ * This hash function is used to distribute rings within the per-domain
+ * hash tables (d->argo->ring_hash and d->argo_send_hash). The hash table
+ * will provide a struct if a match is found with a 'argo_ring_id' key:
+ * ie. the key is a (domain id, argo port, partner domain id) tuple.
+ * The algorithm approximates the string hashing function 'djb2'.
+ */
+static unsigned int
+hash_index(const struct argo_ring_id *id)
+{
+    unsigned int hash = 5381; /* prime constant from djb2 */
+
+    /* For each input: hash = hash * 33 + <new input character value> */
+    hash = ((hash << 5) + hash) +  (id->aport            & 0xff);
+    hash = ((hash << 5) + hash) + ((id->aport      >> 8) & 0xff);
+    hash = ((hash << 5) + hash) + ((id->aport     >> 16) & 0xff);
+    hash = ((hash << 5) + hash) + ((id->aport     >> 24) & 0xff);
+    hash = ((hash << 5) + hash) +  (id->domain_id        & 0xff);
+    hash = ((hash << 5) + hash) + ((id->domain_id  >> 8) & 0xff);
+    hash = ((hash << 5) + hash) +  (id->partner_id       & 0xff);
+    hash = ((hash << 5) + hash) + ((id->partner_id >> 8) & 0xff);
+
+    /*
+     * Since ARGO_HASHTABLE_SIZE is small, use higher-order bits of the
+     * hash to contribute to the lower-order bits before masking off.
+     */
+    return (hash ^ (hash >> 15)) & (ARGO_HASHTABLE_SIZE - 1);
+}
+
+static struct argo_ring_info *
+find_ring_info(const struct domain *d, const struct argo_ring_id *id)
+{
+    struct argo_ring_info *ring_info;
+    const struct list_head *bucket;
+
+    ASSERT(LOCKING_Read_rings_L2(d));
+
+    /* List is not modified here. Search and return the match if found. */
+    bucket = &d->argo->ring_hash[hash_index(id)];
+
+    list_for_each_entry(ring_info, bucket, node)
+    {
+        const struct argo_ring_id *cmpid = &ring_info->id;
+
+        if ( cmpid->aport == id->aport &&
+             cmpid->domain_id == id->domain_id &&
+             cmpid->partner_id == id->partner_id )
+        {
+            argo_dprintk("found ring_info for ring(%u:%x %u)\n",
+                         id->domain_id, id->aport, id->partner_id);
+            return ring_info;
+        }
+    }
+    argo_dprintk("no ring_info 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)
+{
+    unsigned int i;
+
+    ASSERT(LOCKING_L3(d, ring_info));
+
+    if ( !ring_info->mfn_mapping )
+        return;
+
+    ASSERT(!ring_info->nmfns || ring_info->mfns);
+
+    for ( i = 0; i < ring_info->nmfns; i++ )
+    {
+        if ( !ring_info->mfn_mapping[i] )
+            continue;
+
+        ASSERT(!mfn_eq(ring_info->mfns[i], INVALID_MFN));
+        argo_dprintk(XENLOG_ERR "argo: unmapping page %"PRI_mfn" from %p\n",
+                     mfn_x(ring_info->mfns[i]), ring_info->mfn_mapping[i]);
+
+        unmap_domain_page_global(ring_info->mfn_mapping[i]);
+        ring_info->mfn_mapping[i] = NULL;
+    }
+}
+
+static void
+wildcard_pending_list_remove(domid_t domain_id, struct pending_ent *ent)
+{
+    struct domain *d = get_domain_by_id(domain_id);
+
+    if ( !d )
+        return;
+
+    ASSERT(LOCKING_Read_L1);
+
+    if ( d->argo )
+    {
+        spin_lock(&d->argo->wildcard_L2_lock);
+        list_del(&ent->wildcard_node);
+        spin_unlock(&d->argo->wildcard_L2_lock);
+    }
+    put_domain(d);
+}
+
+static void
+pending_remove_all(const struct domain *d, struct argo_ring_info *ring_info)
+{
+    struct pending_ent *ent;
+
+    ASSERT(LOCKING_L3(d, ring_info));
+
+    /* Delete all pending notifications from this ring's list. */
+    while ( (ent = list_first_entry_or_null(&ring_info->pending,
+                                            struct pending_ent, node)) )
+    {
+        /* For wildcard rings, remove each from their wildcard list too. */
+        if ( ring_info->id.partner_id == XEN_ARGO_DOMID_ANY )
+            wildcard_pending_list_remove(ent->domain_id, ent);
+        list_del(&ent->node);
+        xfree(ent);
+    }
+    ring_info->npending = 0;
+}
+
+static void
+wildcard_rings_pending_remove(struct domain *d)
+{
+    struct pending_ent *ent;
+
+    ASSERT(LOCKING_Write_L1);
+
+    /* Delete all pending signals to the domain about wildcard rings. */
+    while ( (ent = list_first_entry_or_null(&d->argo->wildcard_pend_list,
+                                            struct pending_ent, node)) )
+    {
+        /*
+         * The ent->node deleted here, and the npending value decreased,
+         * belong to the ring_info of another domain, which is why this
+         * function requires holding W(L1):
+         * it implies the L3 lock that protects that ring_info struct.
+         */
+        ent->ring_info->npending--;
+        list_del(&ent->node);
+        list_del(&ent->wildcard_node);
+        xfree(ent);
+    }
+}
+
+static void
+ring_remove_mfns(const struct domain *d, struct argo_ring_info *ring_info)
+{
+    unsigned int i;
+
+    ASSERT(LOCKING_Write_rings_L2(d));
+
+    if ( !ring_info->mfns )
+        return;
+
+    if ( !ring_info->mfn_mapping )
+    {
+        ASSERT_UNREACHABLE();
+        return;
+    }
+
+    ring_unmap(d, ring_info);
+
+    for ( i = 0; i < ring_info->nmfns; i++ )
+        if ( !mfn_eq(ring_info->mfns[i], INVALID_MFN) )
+            put_page_and_type(mfn_to_page(ring_info->mfns[i]));
+
+    ring_info->nmfns = 0;
+    XFREE(ring_info->mfns);
+    XFREE(ring_info->mfn_mapping);
+}
+
+static void
+ring_remove_info(const struct domain *d, struct argo_ring_info *ring_info)
+{
+    ASSERT(LOCKING_Write_rings_L2(d));
+
+    pending_remove_all(d, ring_info);
+    list_del(&ring_info->node);
+    ring_remove_mfns(d, ring_info);
+    xfree(ring_info);
+}
+
+static void
+domain_rings_remove_all(struct domain *d)
+{
+    unsigned int i;
+
+    ASSERT(LOCKING_Write_rings_L2(d));
+
+    for ( i = 0; i < ARGO_HASHTABLE_SIZE; ++i )
+    {
+        struct argo_ring_info *ring_info;
+        struct list_head *bucket = &d->argo->ring_hash[i];
+
+        while ( (ring_info = list_first_entry_or_null(bucket,
+                                                      struct argo_ring_info,
+                                                      node)) )
+            ring_remove_info(d, ring_info);
+    }
+    d->argo->ring_count = 0;
+}
+
+/*
+ * Tear down all rings of other domains where src_d domain is the partner.
+ * (ie. it is the single domain that can send to those rings.)
+ * This will also cancel any pending notifications about those rings.
+ */
+static void
+partner_rings_remove(struct domain *src_d)
+{
+    unsigned int i;
+
+    ASSERT(LOCKING_Write_L1);
+
+    for ( i = 0; i < ARGO_HASHTABLE_SIZE; ++i )
+    {
+        struct argo_send_info *send_info;
+        struct list_head *bucket = &src_d->argo->send_hash[i];
+
+        /* Remove all ents from the send list. Take each off their ring list. 
*/
+        while ( (send_info = list_first_entry_or_null(bucket,
+                                                      struct argo_send_info,
+                                                      node)) )
+        {
+            struct domain *dst_d = get_domain_by_id(send_info->id.domain_id);
+
+            if ( dst_d && dst_d->argo )
+            {
+                struct argo_ring_info *ring_info =
+                    find_ring_info(dst_d, &send_info->id);
+
+                if ( ring_info )
+                {
+                    ring_remove_info(dst_d, ring_info);
+                    dst_d->argo->ring_count--;
+                }
+                else
+                    ASSERT_UNREACHABLE();
+            }
+            else
+                ASSERT_UNREACHABLE();
+
+            if ( dst_d )
+                put_domain(dst_d);
+
+            list_del(&send_info->node);
+            xfree(send_info);
+        }
+    }
+}
+
 long
 do_argo_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg1,
            XEN_GUEST_HANDLE_PARAM(void) arg2, unsigned long raw_arg3,
            unsigned long raw_arg4)
 {
-    return -ENOSYS;
+    long rc;
+    unsigned int arg3 = raw_arg3, arg4 = raw_arg4;
+
+    argo_dprintk("->do_argo_op(%u,%p,%p,%lu,0x%lx)\n", cmd,
+                 (void *)arg1.p, (void *)arg2.p, raw_arg3, raw_arg4);
+
+    /* Reject numeric hypercall args outside 32-bit range */
+    if ( (arg3 != raw_arg3) || (arg4 != raw_arg4) )
+        return -EINVAL;
+
+    if ( unlikely(!opt_argo) )
+        return -EOPNOTSUPP;
+
+    switch ( cmd )
+    {
+    default:
+        rc = -EOPNOTSUPP;
+        break;
+    }
+
+    argo_dprintk("<-do_argo_op(%u)=%ld\n", cmd, rc);
+
+    return rc;
 }
 
 #ifdef CONFIG_COMPAT
@@ -42,6 +572,96 @@ compat_argo_op(unsigned int cmd, 
XEN_GUEST_HANDLE_PARAM(void) arg1,
                XEN_GUEST_HANDLE_PARAM(void) arg2, unsigned long arg3,
                unsigned long arg4)
 {
-    return -ENOSYS;
+    /* Forward all ops to the native handler */
+    return do_argo_op(cmd, arg1, arg2, arg3, arg4);
 }
 #endif
+
+static void
+argo_domain_init(struct argo_domain *argo)
+{
+    unsigned int i;
+
+    rwlock_init(&argo->rings_L2_rwlock);
+    spin_lock_init(&argo->send_L2_lock);
+    spin_lock_init(&argo->wildcard_L2_lock);
+
+    for ( i = 0; i < ARGO_HASHTABLE_SIZE; ++i )
+    {
+        INIT_LIST_HEAD(&argo->ring_hash[i]);
+        INIT_LIST_HEAD(&argo->send_hash[i]);
+    }
+    INIT_LIST_HEAD(&argo->wildcard_pend_list);
+}
+
+int
+argo_init(struct domain *d)
+{
+    struct argo_domain *argo;
+
+    if ( !opt_argo )
+    {
+        argo_dprintk("argo disabled, domid: %u\n", d->domain_id);
+        return 0;
+    }
+
+    argo_dprintk("init: domid: %u\n", d->domain_id);
+
+    argo = xzalloc(struct argo_domain);
+    if ( !argo )
+        return -ENOMEM;
+
+    argo_domain_init(argo);
+
+    write_lock(&L1_global_argo_rwlock);
+
+    d->argo = argo;
+
+    write_unlock(&L1_global_argo_rwlock);
+
+    return 0;
+}
+
+void
+argo_destroy(struct domain *d)
+{
+    BUG_ON(!d->is_dying);
+
+    write_lock(&L1_global_argo_rwlock);
+
+    argo_dprintk("destroy: domid %u d->argo=%p\n", d->domain_id, d->argo);
+
+    if ( d->argo )
+    {
+        domain_rings_remove_all(d);
+        partner_rings_remove(d);
+        wildcard_rings_pending_remove(d);
+        XFREE(d->argo);
+    }
+
+    write_unlock(&L1_global_argo_rwlock);
+}
+
+void
+argo_soft_reset(struct domain *d)
+{
+    write_lock(&L1_global_argo_rwlock);
+
+    argo_dprintk("soft reset d=%u d->argo=%p\n", d->domain_id, d->argo);
+
+    if ( d->argo )
+    {
+        domain_rings_remove_all(d);
+        partner_rings_remove(d);
+        wildcard_rings_pending_remove(d);
+
+        /*
+         * Since opt_argo cannot change at runtime, if d->argo is true then
+         * opt_argo must be true, and we can assume that init is allowed to
+         * proceed again here.
+         */
+        argo_domain_init(d->argo);
+    }
+
+    write_unlock(&L1_global_argo_rwlock);
+}
diff --git a/xen/common/domain.c b/xen/common/domain.c
index c623dae..7470cd9 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -32,6 +32,7 @@
 #include <xen/grant_table.h>
 #include <xen/xenoprof.h>
 #include <xen/irq.h>
+#include <xen/argo.h>
 #include <asm/debugger.h>
 #include <asm/p2m.h>
 #include <asm/processor.h>
@@ -277,6 +278,8 @@ static void _domain_destroy(struct domain *d)
 
     xfree(d->pbuf);
 
+    argo_destroy(d);
+
     rangeset_domain_destroy(d);
 
     free_cpumask_var(d->dirty_cpumask);
@@ -445,6 +448,9 @@ struct domain *domain_create(domid_t domid,
             goto fail;
         init_status |= INIT_gnttab;
 
+        if ( (err = argo_init(d)) != 0 )
+            goto fail;
+
         err = -ENOMEM;
 
         d->pbuf = xzalloc_array(char, DOMAIN_PBUF_SIZE);
@@ -717,6 +723,7 @@ int domain_kill(struct domain *d)
         if ( d->is_dying != DOMDYING_alive )
             return domain_kill(d);
         d->is_dying = DOMDYING_dying;
+        argo_destroy(d);
         evtchn_destroy(d);
         gnttab_release_mappings(d);
         tmem_destroy(d->tmem_client);
@@ -1175,6 +1182,8 @@ int domain_soft_reset(struct domain *d)
 
     grant_table_warn_active_grants(d);
 
+    argo_soft_reset(d);
+
     for_each_vcpu ( d, v )
     {
         set_xen_guest_handle(runstate_guest(v), NULL);
diff --git a/xen/include/Makefile b/xen/include/Makefile
index f7895e4..3d14532 100644
--- a/xen/include/Makefile
+++ b/xen/include/Makefile
@@ -5,6 +5,7 @@ ifneq ($(CONFIG_COMPAT),)
 compat-arch-$(CONFIG_X86) := x86_32
 
 headers-y := \
+    compat/argo.h \
     compat/callback.h \
     compat/elfnote.h \
     compat/event_channel.h \
diff --git a/xen/include/public/argo.h b/xen/include/public/argo.h
new file mode 100644
index 0000000..530bb82
--- /dev/null
+++ b/xen/include/public/argo.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ * Argo : Hypervisor-Mediated data eXchange
+ *
+ * Derived from v4v, the version 2 of v2v.
+ *
+ * Copyright (c) 2010, Citrix Systems
+ * Copyright (c) 2018-2019, BAE Systems
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __XEN_PUBLIC_ARGO_H__
+#define __XEN_PUBLIC_ARGO_H__
+
+#include "xen.h"
+
+#define XEN_ARGO_DOMID_ANY       DOMID_INVALID
+
+/* Fixed-width type for "argo port" number. Nothing to do with evtchns. */
+typedef uint32_t xen_argo_port_t;
+
+typedef struct xen_argo_addr
+{
+    xen_argo_port_t aport;
+    domid_t domain_id;
+    uint16_t pad;
+} xen_argo_addr_t;
+
+typedef struct xen_argo_ring
+{
+    /* Guests should use atomic operations to access rx_ptr */
+    uint32_t rx_ptr;
+    /* Guests should use atomic operations to access tx_ptr */
+    uint32_t tx_ptr;
+    /*
+     * Header space reserved for later use. Align the start of the ring to a
+     * multiple of the message slot size.
+     */
+    uint8_t reserved[56];
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+    uint8_t ring[];
+#elif defined(__GNUC__)
+    uint8_t ring[0];
+#endif
+} xen_argo_ring_t;
+
+#endif
diff --git a/xen/include/xen/argo.h b/xen/include/xen/argo.h
new file mode 100644
index 0000000..2ba7e5c
--- /dev/null
+++ b/xen/include/xen/argo.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+ * Argo : Hypervisor-Mediated data eXchange
+ *
+ * Copyright (c) 2018, BAE Systems
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __XEN_ARGO_H__
+#define __XEN_ARGO_H__
+
+#include <xen/sched.h>
+
+#ifdef CONFIG_ARGO
+
+int argo_init(struct domain *d);
+void argo_destroy(struct domain *d);
+void argo_soft_reset(struct domain *d);
+
+#else /* !CONFIG_ARGO */
+
+static inline int argo_init(struct domain *d)
+{
+    return 0;
+}
+
+static inline void argo_destroy(struct domain *d)
+{
+}
+
+static inline void argo_soft_reset(struct domain *d)
+{
+}
+
+#endif
+
+#endif
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 4956a77..6e69afa 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -490,6 +490,11 @@ struct domain
         unsigned int guest_request_enabled       : 1;
         unsigned int guest_request_sync          : 1;
     } monitor;
+
+#ifdef CONFIG_ARGO
+    /* Argo interdomain communication support */
+    struct argo_domain *argo;
+#endif
 };
 
 /* Protect updates/reads (resp.) of domain_list and domain_hash. */
diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst
index 5273320..16601d9 100644
--- a/xen/include/xlat.lst
+++ b/xen/include/xlat.lst
@@ -30,6 +30,8 @@
 ?      mc_notifydomain                 arch-x86/xen-mca.h
 !      mc_physcpuinfo                  arch-x86/xen-mca.h
 ?      page_offline_action             arch-x86/xen-mca.h
+?      argo_addr                       argo.h
+?      argo_ring                       argo.h
 ?      evtchn_alloc_unbound            event_channel.h
 ?      evtchn_bind_interdomain         event_channel.h
 ?      evtchn_bind_ipi                 event_channel.h
-- 
2.7.4


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.