diff --git a/README.md b/README.md index 64c67b1..dc13f1a 100644 --- a/README.md +++ b/README.md @@ -286,6 +286,37 @@ Examples 1: 20 10 ``` + - Run the current kernel creating 4 NUMA nodes of 1GB each and assign + different distance costs between the NUMA nodes to simulate non-uniform + memory access: +``` + $ vng -r --cpu 8 -m 4G \ + > --numa 1G,cpus=0-1 --numa 1G,cpus=2-3 \ + > --numa 1G,cpus=4-5 --numa 1G,cpus=6-7 \ + > --numa-distance 0,1=51 --numa-distance 0,2=31 --numa-distance 0,3=41 \ + > --numa-distance 1,2=21 --numa-distance 1,3=61 \ + > --numa-distance 2,3=11 -- numactl -H + available: 4 nodes (0-3) + node 0 cpus: 0 1 + node 0 size: 1006 MB + node 0 free: 974 MB + node 1 cpus: 2 3 + node 1 size: 953 MB + node 1 free: 919 MB + node 2 cpus: 4 5 + node 2 size: 943 MB + node 2 free: 894 MB + node 3 cpus: 6 7 + node 3 size: 1006 MB + node 3 free: 965 MB + node distances: + node 0 1 2 3 + 0: 10 51 31 41 + 1: 51 10 21 61 + 2: 31 21 10 11 + 3: 41 61 11 10 +``` + - Run `glxgears` inside a kernel recompiled in the current directory: ``` $ vng -g -- glxgears diff --git a/virtme/commands/run.py b/virtme/commands/run.py index 9cb986c..6f7a926 100644 --- a/virtme/commands/run.py +++ b/virtme/commands/run.py @@ -169,6 +169,12 @@ def make_parser() -> argparse.ArgumentParser: default=None, help="Create NUMA nodes in the guest.", ) + g.add_argument( + "--numa-distance", + action="append", + default=None, + help="Define a distance between two NUMA nodes in the guest (src=ID,dst=ID,val=NUM).", + ) g.add_argument( "--cpus", action="store", default=None, help="Set guest cpu and qemu -smp flag." ) @@ -1042,6 +1048,12 @@ def do_it() -> int: "-numa", f"node,memdev=mem{i}{cpus}" ]) + if args.numa_distance: + for arg in args.numa_distance: + qemuargs.extend([ + "-numa", f"dist,{arg}" + ]) + if args.snaps: if args.root == "/": snapd_state = "/var/lib/snapd/state.json" diff --git a/virtme_ng/run.py b/virtme_ng/run.py index 80b57f9..846f67f 100644 --- a/virtme_ng/run.py +++ b/virtme_ng/run.py @@ -339,6 +339,15 @@ def make_parser(): + "This option implicitly disables the microvm architecture." ) + parser.add_argument( + "--numa-distance", + metavar="SRC,DST=VAL", + action="append", + help="Set a distance of VAL between NUMA node SRC_NODE and DST_NODE. " + + "Use this option multiple times to define multiple distances between NUMA nodes. " + + "This option is used only together with --numa." + ) + parser.add_argument( "--balloon", action="store_true", @@ -1084,6 +1093,25 @@ def _get_virtme_numa(self, args): else: self.virtme_param["numa"] = "" + def _get_virtme_numa_distance(self, args): + if args.numa_distance is not None: + if not args.numa: + arg_fail("error: --numa-distance can be used only with --numa", show_usage=False) + numa_dist_str = "" + for arg in args.numa_distance: + try: + nodes = arg.split('=') + src, dst = nodes[0].split(',') + val = nodes[1] + numa_dist_str += f" --numa-distance src={src},dst={dst},val={val}" + except ValueError: + err_msg = f"error: invalid distance '{arg}', " + \ + "NUMA distance string must be in the format SRC,DST=VAL" + arg_fail(err_msg, show_usage=False) + self.virtme_param["numa_distance"] = numa_dist_str + else: + self.virtme_param["numa_distance"] = "" + def _get_virtme_balloon(self, args): if args.balloon: self.virtme_param["balloon"] = "--balloon" @@ -1178,6 +1206,7 @@ def run(self, args): self._get_virtme_cpus(args) self._get_virtme_memory(args) self._get_virtme_numa(args) + self._get_virtme_numa_distance(args) self._get_virtme_balloon(args) self._get_virtme_gdb(args) self._get_virtme_snaps(args) @@ -1221,6 +1250,7 @@ def run(self, args): + f'{self.virtme_param["cpus"]} ' + f'{self.virtme_param["memory"]} ' + f'{self.virtme_param["numa"]} ' + + f'{self.virtme_param["numa_distance"]} ' + f'{self.virtme_param["balloon"]} ' + f'{self.virtme_param["gdb"]} ' + f'{self.virtme_param["snaps"]} '