--- /dev/null
+#include <linux/memblock.h>
+#include <xen/xen.h>
+#include <xen/interface/memory.h>
+#include <asm/xen/hypercall.h>
+
+
+int __init xen_pv_numa_init(void)
+{
+ int i, j, rc;
+ unsigned int nr_vcpu, nr_dist, nr_memr;
+ phys_addr_t phys_dist = 0, phys_vcpu = 0, phys_memr = 0;
+ unsigned int *virt_dist, *virt_vcpu;
+ struct vmemrange *virt_memr;
+
+ struct vnuma_topology_info numa_topo = {
+ .domid = DOMID_SELF,
+ };
+
+ phys_dist = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ phys_vcpu = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ phys_memr = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+
+ if (!phys_dist || !phys_vcpu || !phys_memr) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ virt_dist = __va(phys_dist);
+ virt_vcpu = __va(phys_vcpu);
+ virt_memr = __va(phys_memr);
+
+ nr_dist = PAGE_SIZE / sizeof(unsigned int);
+ nr_vcpu = PAGE_SIZE / sizeof(unsigned int);
+ nr_memr = PAGE_SIZE / sizeof(struct vmemrange);
+
+ /* Cap nr_vcpu */
+ nr_vcpu = nr_vcpu < setup_max_cpus ? nr_vcpu : setup_max_cpus;
+ nr_vcpu = nr_vcpu < num_possible_cpus() ?
+ nr_vcpu : num_possible_cpus();
+
+ /* As the nr_dist == nr_vnodes * nr_vnodes (by definition), we
+ * have room for maximum sqrt(nr_dist) vnodes.
+ *
+ * In x86, that is sqrt(PAGE_SIZE / sizeof(unsigned int)) =
+ * sqrt(4096 / 4) = sqrt(1024) = 32.
+ */
+ numa_topo.nr_vnodes = 32;
+ numa_topo.nr_vcpus = nr_vcpu;
+ numa_topo.nr_vmemranges = nr_memr;
+
+ set_xen_guest_handle(numa_topo.vdistance.h, virt_dist);
+ set_xen_guest_handle(numa_topo.vcpu_to_vnode.h, virt_vcpu);
+ set_xen_guest_handle(numa_topo.vmemrange.h, virt_memr);
+
+ rc = HYPERVISOR_memory_op(XENMEM_get_vnuma_info, &numa_topo);
+ if (rc < 0)
+ goto out;
+
+ for (i = 0; i < numa_topo.nr_vmemranges; i++) {
+ struct vmemrange *v = &numa_topo.vmemrange.h[i];
+
+ rc = numa_add_memblk(v->nid, v->start, v->end);
+ if (rc) goto out;
+ node_set(v->nid, numa_nodes_parsed);
+ }
+
+ setup_nr_node_ids();
+
+ BUG_ON(numa_topo.nr_vcpus != nr_vcpu);
+
+ for (i = 0; i < numa_topo.nr_vcpus; i++) {
+ unsigned int *map = numa_topo.vcpu_to_vnode.h;
+
+ set_apicid_to_node(i, map[i]);
+ numa_set_node(i, map[i]);
+ cpumask_set_cpu(i, node_to_cpumask_map[map[i]]);
+ }
+
+ for (i = 0; i < numa_topo.nr_vnodes; i++) {
+ for (j = 0; j < numa_topo.nr_vnodes; j++) {
+ unsigned int idx = (i * numa_topo.nr_vnodes) + j;
+ numa_set_distance(i, j, *(virt_dist + idx));
+ }
+ }
+
+ rc = 0;
+
+out:
+ /* Free resources */
+ if (phys_dist) memblock_free(__pa(phys_dist), PAGE_SIZE);
+ if (phys_vcpu) memblock_free(__pa(phys_vcpu), PAGE_SIZE);
+ if (phys_memr) memblock_free(__pa(phys_memr), PAGE_SIZE);
+
+ return rc;
+}