From patchwork Thu May 30 22:06:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierrick Bouvier X-Patchwork-Id: 800214 Delivered-To: patch@linaro.org Received: by 2002:a5d:65c4:0:b0:35b:5a80:51b4 with SMTP id e4csp448120wrw; Thu, 30 May 2024 15:07:29 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCWzR6GIAQqjalPmx6XfpnlGiIn71YyOgOMy5/+UQAo6Rd4ow8nCKCUHcSiZl46aMT5SBhUk/yAZ4xGzIw0GTnry X-Google-Smtp-Source: AGHT+IF1E7BPPH/f2Z9wwSbYembldZjkoKAZua7PMd/uy8Y+sr5bJE1e26aNWiM5a9tFOSNWbTtE X-Received: by 2002:a05:6870:71c8:b0:24f:f23e:dd50 with SMTP id 586e51a60fabf-2508bc51dbcmr215299fac.57.1717106849190; Thu, 30 May 2024 15:07:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1717106849; cv=none; d=google.com; s=arc-20160816; b=aj4zNOjz5YRxdu11EgqGAdYtQIJM3ll/ctEpcttf5uXHmZW+uu6L+MSYQiHGGw/qfc Y7MkHoy2DShU6/ti1QgFZs7WtZ7QSL5hEjhF1M4c834fVN7yGUadzpc2Vfa9tIS/gd/1 Dr8GcztFevpYqD9pGQBspk4wo51bt4THvILZBnlKw/04b/S0D0PJtyjDA90PPrkowSQc 1wHWJ4zJuwW72hTLCPfNBzmwNKuUscmIA/0Jd6CqN3OmU9ZEYpMAE5nYC1a5a0C+Caxe Zc9+TQjkE5JGRm/RBhLJ2rIkV4G+EIYJxvblR5apSCyyK65+luleUn9I3UGYvMZneHNe KgXg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=3EC/ILC9E1z+FmTz2M5OzLffDsaNAiYhutjbzlwCvcM=; fh=9DrfyMIxQXmVFGXijTr0lAFjSMU1v2ifvPRi5ZRi3rM=; b=OFDHir5pl8d3sJZ6ofrnzi65+SM3jnmBhILyGacqdRC4hs/rkKbWyrnGBqDbbN04Pe iyKuLLVj+C5BNC6GA5M8pi0VQ7B11kXwLs8WmsK/PXYa988u1JT0eiYonkdxc1pBJRu7 o4R9hMiVhX9zTGfL5nkRv7HDJrQkJ0l0f4DPwfNOWGI0cJ5alg6RHtOn5nVpIutFAvb2 PmnCCibG/2xiAqfzPUMkbVUYX8VTOahKebJ/Ehk0Yfku8MHIOD/VNH9hZk2fg54MQjyW JWLuPWIIjvfN/HuCvgJ9duQiCJ/fyfmvGGoSjup8YYGxksK1j9IvdC3biZLUf1awmPOG c9pw==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=MgdCbOPQ; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org. [209.51.188.17]) by mx.google.com with ESMTPS id af79cd13be357-794f2f1738dsi65941485a.170.2024.05.30.15.07.29 for (version=TLS1_2 cipher=ECDHE-ECDSA-CHACHA20-POLY1305 bits=256/256); Thu, 30 May 2024 15:07:29 -0700 (PDT) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=MgdCbOPQ; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom="qemu-devel-bounces+patch=linaro.org@nongnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sCnuq-0007Lu-C7; Thu, 30 May 2024 18:06:32 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sCnum-0007KV-Sr for qemu-devel@nongnu.org; Thu, 30 May 2024 18:06:28 -0400 Received: from mail-pl1-x62f.google.com ([2607:f8b0:4864:20::62f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sCnuk-0005EN-Io for qemu-devel@nongnu.org; Thu, 30 May 2024 18:06:28 -0400 Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-1f4a0050b9aso3138915ad.2 for ; Thu, 30 May 2024 15:06:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1717106783; x=1717711583; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=3EC/ILC9E1z+FmTz2M5OzLffDsaNAiYhutjbzlwCvcM=; b=MgdCbOPQJioOn9zobO0FZqGGjuVsT/bxlALyd+wDzV8CF+i89/ia2BybWljuS8kn1j 0VTnVfjMQwVsE+dQkavNfSAFYzGA2x0KZXl25txoYKL6UL4edHYu36w4qb73UrmvG8iO YHNlN5jwELjOPjh1p47bp93Dpo5LPxmca/IY2jVepTM3EpsegF7BwpqjRBiboXMSD8Tt kiZzGUA00sBe6PRcU75FgVz9ZTz8gKd6HbxSbt03BFK/ft8MmPGaLzT9O1q+GqcdqjIN tz7MbVMcO5t21GFt0aB4NribxtzFACWkYr0/0I9oDmkiodzsbQZvsTOBt8ebcjtxiyqN 6UXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717106783; x=1717711583; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=3EC/ILC9E1z+FmTz2M5OzLffDsaNAiYhutjbzlwCvcM=; b=vzHPEQkwmRd40HmjKP/tkyyUrQFXbAsBLpFUlll4ydmhAU7lgl183YgrKf52djs5YV /8CuWe9ehf0YN354hk01/DZP+irxhnS5tnJe31fWBX/aUvLSwu5SBAN5iucVaYjfo7gF Yb0KQMjOFfHzdociHUL1AxUmnGVzKVhkRBShpzK1I6Jv9Dtv2rJ8+aJS1GwiHXPCu80e tDTUJf6PAwZ61SOaWXkkpMDqIA8JQWtxXjpsEyOcQ5PuU8L759iYLKyCNUSbAr6r62Q9 Yq+WXy4TSqTNByv27isesLJOOkzKbfW0cz0ePdcZYKPrMOjwip9huqk5S94NueFTmLlj fSXg== X-Gm-Message-State: AOJu0YxS3qlVpypwm7cCTx0tFbMsYNgn3sOxafy9eWw+pFYT3gngaMuI cYlIjzo4nYG++22mEERNplj3bvkW2TL6hkX7qSsTVohzUqJBuDrtKe98dKTd9O0eKH+GqBBacO4 L3ak= X-Received: by 2002:a17:902:e5c9:b0:1f4:75e8:2e9c with SMTP id d9443c01a7336-1f636fd22cemr1915875ad.11.1717106783300; Thu, 30 May 2024 15:06:23 -0700 (PDT) Received: from linaro.vn.shawcable.net ([2604:3d08:9384:1d00::e697]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1f6323dde39sm2732615ad.167.2024.05.30.15.06.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 May 2024 15:06:22 -0700 (PDT) From: Pierrick Bouvier To: qemu-devel@nongnu.org Cc: Thomas Huth , Laurent Vivier , Pierrick Bouvier , Paolo Bonzini , Alexandre Iooss , =?utf-8?q?Alex_Benn=C3=A9e?= , Mahmoud Mandour , Richard Henderson Subject: [PATCH v3 6/6] contrib/plugins: add ips plugin example for cost modeling Date: Thu, 30 May 2024 15:06:10 -0700 Message-Id: <20240530220610.1245424-7-pierrick.bouvier@linaro.org> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240530220610.1245424-1-pierrick.bouvier@linaro.org> References: <20240530220610.1245424-1-pierrick.bouvier@linaro.org> MIME-Version: 1.0 Received-SPF: pass client-ip=2607:f8b0:4864:20::62f; envelope-from=pierrick.bouvier@linaro.org; helo=mail-pl1-x62f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: qemu-devel-bounces+patch=linaro.org@nongnu.org This plugin uses the new time control interface to make decisions about the state of time during the emulation. The algorithm is currently very simple. The user specifies an ips rate which applies per core. If the core runs ahead of its allocated execution time the plugin sleeps for a bit to let real time catch up. Either way time is updated for the emulation as a function of total executed instructions with some adjustments for cores that idle. Examples -------- Slow down execution of /bin/true: $ num_insn=$(./build/qemu-x86_64 -plugin ./build/tests/plugin/libinsn.so -d plugin /bin/true |& grep total | sed -e 's/.*: //') $ time ./build/qemu-x86_64 -plugin ./build/contrib/plugins/libips.so,ips=$(($num_insn/4)) /bin/true real 4.000s Boot a Linux kernel simulating a 250MHz cpu: $ /build/qemu-system-x86_64 -kernel /boot/vmlinuz-6.1.0-21-amd64 -append "console=ttyS0" -plugin ./build/contrib/plugins/libips.so,ips=$((250*1000*1000)) -smp 1 -m 512 check time until kernel panic on serial0 Tested in system mode by booting a full debian system, and using: $ sysbench cpu run Performance decrease linearly with the given number of ips. Signed-off-by: Pierrick Bouvier --- contrib/plugins/ips.c | 164 +++++++++++++++++++++++++++++++++++++++ contrib/plugins/Makefile | 1 + 2 files changed, 165 insertions(+) create mode 100644 contrib/plugins/ips.c diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c new file mode 100644 index 00000000000..db77729264b --- /dev/null +++ b/contrib/plugins/ips.c @@ -0,0 +1,164 @@ +/* + * ips rate limiting plugin. + * + * This plugin can be used to restrict the execution of a system to a + * particular number of Instructions Per Second (ips). This controls + * time as seen by the guest so while wall-clock time may be longer + * from the guests point of view time will pass at the normal rate. + * + * This uses the new plugin API which allows the plugin to control + * system time. + * + * Copyright (c) 2023 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +/* how many times do we update time per sec */ +#define NUM_TIME_UPDATE_PER_SEC 10 +#define NSEC_IN_ONE_SEC (1000 * 1000 * 1000) + +static GMutex global_state_lock; + +static uint64_t max_insn_per_second = 1000 * 1000 * 1000; /* ips per core, per second */ +static uint64_t max_insn_per_quantum; /* trap every N instructions */ +static int64_t virtual_time_ns; /* last set virtual time */ + +static const void *time_handle; + +typedef struct { + uint64_t total_insn; + uint64_t quantum_insn; /* insn in last quantum */ + int64_t last_quantum_time; /* time when last quantum started */ +} vCPUTime; + +struct qemu_plugin_scoreboard *vcpus; + +/* return epoch time in ns */ +static int64_t now_ns(void) +{ + return g_get_real_time() * 1000; +} + +static uint64_t num_insn_during(int64_t elapsed_ns) +{ + double num_secs = elapsed_ns / (double) NSEC_IN_ONE_SEC; + return num_secs * (double) max_insn_per_second; +} + +static int64_t time_for_insn(uint64_t num_insn) +{ + double num_secs = (double) num_insn / (double) max_insn_per_second; + return num_secs * (double) NSEC_IN_ONE_SEC; +} + +static void update_system_time(vCPUTime *vcpu) +{ + int64_t elapsed_ns = now_ns() - vcpu->last_quantum_time; + uint64_t max_insn = num_insn_during(elapsed_ns); + + if (vcpu->quantum_insn >= max_insn) { + /* this vcpu ran faster than expected, so it has to sleep */ + uint64_t insn_advance = vcpu->quantum_insn - max_insn; + uint64_t time_advance_ns = time_for_insn(insn_advance); + int64_t sleep_us = time_advance_ns / 1000; + g_usleep(sleep_us); + } + + vcpu->total_insn += vcpu->quantum_insn; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); + + /* based on total number of instructions, what should be the new time? */ + int64_t new_virtual_time = time_for_insn(vcpu->total_insn); + + g_mutex_lock(&global_state_lock); + + /* Time only moves forward. Another vcpu might have updated it already. */ + if (new_virtual_time > virtual_time_ns) { + qemu_plugin_update_ns(time_handle, new_virtual_time); + virtual_time_ns = new_virtual_time; + } + + g_mutex_unlock(&global_state_lock); +} + +static void vcpu_init(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + vcpu->total_insn = 0; + vcpu->quantum_insn = 0; + vcpu->last_quantum_time = now_ns(); +} + +static void vcpu_exit(qemu_plugin_id_t id, unsigned int cpu_index) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + update_system_time(vcpu); +} + +static void every_quantum_insn(unsigned int cpu_index, void *udata) +{ + vCPUTime *vcpu = qemu_plugin_scoreboard_find(vcpus, cpu_index); + g_assert(vcpu->quantum_insn >= max_insn_per_quantum); + update_system_time(vcpu); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + size_t n_insns = qemu_plugin_tb_n_insns(tb); + qemu_plugin_u64 quantum_insn = + qemu_plugin_scoreboard_u64_in_struct(vcpus, vCPUTime, quantum_insn); + /* count (and eventually trap) once per tb */ + qemu_plugin_register_vcpu_tb_exec_inline_per_vcpu( + tb, QEMU_PLUGIN_INLINE_ADD_U64, quantum_insn, n_insns); + qemu_plugin_register_vcpu_tb_exec_cond_cb( + tb, every_quantum_insn, + QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_COND_GE, + quantum_insn, max_insn_per_quantum, NULL); +} + +static void plugin_exit(qemu_plugin_id_t id, void *udata) +{ + qemu_plugin_scoreboard_free(vcpus); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "ips") == 0) { + max_insn_per_second = g_ascii_strtoull(tokens[1], NULL, 10); + if (!max_insn_per_second && errno) { + fprintf(stderr, "%s: couldn't parse %s (%s)\n", + __func__, tokens[1], g_strerror(errno)); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + vcpus = qemu_plugin_scoreboard_new(sizeof(vCPUTime)); + max_insn_per_quantum = max_insn_per_second / NUM_TIME_UPDATE_PER_SEC; + + time_handle = qemu_plugin_request_time_control(); + g_assert(time_handle); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_vcpu_init_cb(id, vcpu_init); + qemu_plugin_register_vcpu_exit_cb(id, vcpu_exit); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + + return 0; +} diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 0b64d2c1e3a..449ead11305 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -27,6 +27,7 @@ endif NAMES += hwprofile NAMES += cache NAMES += drcov +NAMES += ips ifeq ($(CONFIG_WIN32),y) SO_SUFFIX := .dll