Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177440138
D61371.1794399275.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
21 KB
Referenced Files
None
Subscribers
None
D61371.1794399275.diff
View Options
diff --git a/share/man/man4/hwpmc.4 b/share/man/man4/hwpmc.4
--- a/share/man/man4/hwpmc.4
+++ b/share/man/man4/hwpmc.4
@@ -428,7 +428,7 @@
.Bl -tag -width indent
.It Va kern.hwpmc.callchaindepth Pq integer, read-only
The maximum number of call chain records to capture per sample.
-The default is 8.
+The default is 128.
.It Va kern.hwpmc.debugflags Pq string, read-write
(Only available if the
.Nm
@@ -458,6 +458,18 @@
logging function.
The default buffer size is 256KB.
The maximum value is 16MB.
+.It Va kern.hwpmc.max_sample_rate Pq integer, read-write
+The maximum number of samples each second for one sampling PMC on one CPU.
+The driver divides this value by
+.Va hz
+to get a limit for each clock tick.
+When a PMC interrupts more times than this limit in one clock tick, the
+driver drops the sample, stops the PMC, and counts the drop.
+The driver starts the PMC again at the next clock tick.
+The value 0 disables the limit.
+Without the limit, a short sample period can use all the CPU time in the
+interrupt handler.
+The default is 100000.
.It Va kern.hwpmc.mincount Pq integer, read-write
The minimum sampling rate for sampling mode PMCs.
The default count is 1000 events.
@@ -476,7 +488,7 @@
must not exceed 32MB per CPU.
.It Va kern.hwpmc.nsamples Pq integer, read-only
The number of entries in the per-CPU ring buffer used during sampling.
-The default is 512.
+The default is 256.
.It Va security.bsd.unprivileged_syspmcs Pq boolean, read-write
If set to non-zero, allow unprivileged processes to allocate system-wide
PMCs.
@@ -536,6 +548,10 @@
The module loading process failed because the currently executing kernel
was not configured with the required configuration option
.Dv HWPMC_HOOKS .
+.It "hwpmc: sample rate limit disabled, sampling can stall the system"
+An administrator set
+.Va kern.hwpmc.max_sample_rate
+to 0.
.It "hwpmc: tunable hashsize=%d must be greater than zero."
A negative value was supplied for tunable
.Va kern.hwpmc.hashsize .
diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c
--- a/sys/dev/hwpmc/hwpmc_logging.c
+++ b/sys/dev/hwpmc/hwpmc_logging.c
@@ -896,9 +896,9 @@
spinlock_enter();
cpu = curcpu;
- /* tell hardclock not to run again */
+ /* Non-NULL means flush only. */
if (PMC_CPU_HAS_SAMPLES(cpu))
- PMC_CALL_HOOK_UNLOCKED(curthread, PMC_FN_DO_SAMPLES, NULL);
+ PMC_CALL_HOOK_UNLOCKED(curthread, PMC_FN_DO_SAMPLES, po);
plb = po->po_curbuf[cpu];
if (plb && plb->plb_ptr != plb->plb_base)
diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c
--- a/sys/dev/hwpmc/hwpmc_mod.c
+++ b/sys/dev/hwpmc/hwpmc_mod.c
@@ -200,6 +200,7 @@
static int pmc_debugflags_sysctl_handler(SYSCTL_HANDLER_ARGS);
static int pmc_debugflags_parse(char *newstr, char *fence);
#endif
+static int pmc_max_sample_rate_sysctl_handler(SYSCTL_HANDLER_ARGS);
static void pmc_multipart_add(struct pmc_sample *ps, int type,
int length);
@@ -265,6 +266,7 @@
static void pmc_release_pmc_descriptor(struct pmc *pmc);
static void pmc_remove_owner(struct pmc_owner *po);
static void pmc_remove_process_descriptor(struct pmc_process *pp);
+static void pmc_restart_stalled(int cpu);
static int pmc_start(struct pmc *pm);
static int pmc_stop(struct pmc *pm);
static int pmc_syscall_handler(struct thread *td, void *syscall_args);
@@ -273,6 +275,7 @@
static void pmc_thread_descriptor_pool_free(struct pmc_thread *pt);
static void pmc_unlink_target_process(struct pmc *pmc,
struct pmc_process *pp);
+static void pmc_update_sample_limit(void);
static int generic_switch_in(struct pmc_cpu *pc, struct pmc_process *pp);
static int generic_switch_out(struct pmc_cpu *pc, struct pmc_process *pp);
@@ -318,6 +321,19 @@
SYSCTL_COUNTER_U64(_kern_hwpmc_stats, OID_AUTO, overwrites, CTLFLAG_RW,
&pmc_stats.pm_overwrites,
"# of times a sample was overwritten before being logged");
+SYSCTL_COUNTER_U64(_kern_hwpmc_stats, OID_AUTO, intr_throttled, CTLFLAG_RW,
+ &pmc_stats.pm_intr_throttled,
+ "# of times a PMC stopped at the sample rate limit");
+/* Per-PMC, per-CPU rate limit. Zero disables it. */
+static u_int pmc_max_sample_rate = PMC_MAX_SAMPLE_RATE;
+static u_int pmc_max_samples_per_tick;
+SYSCTL_PROC(_kern_hwpmc, OID_AUTO, max_sample_rate,
+ CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, 0,
+ pmc_max_sample_rate_sysctl_handler, "IU",
+ "maximum samples per second for one PMC on one CPU (0: no limit)");
+
+DPCPU_DEFINE_STATIC(int, pmc_tick_seq); /* Tick number. */
+DPCPU_DEFINE_STATIC(uint8_t, pmc_throttled); /* Restart next tick. */
static int pmc_callchaindepth = PMC_CALLCHAIN_DEPTH;
SYSCTL_INT(_kern_hwpmc, OID_AUTO, callchaindepth, CTLFLAG_RDTUN,
@@ -612,6 +628,36 @@
}
#endif
+/* Rate limit. */
+static void
+pmc_update_sample_limit(void)
+{
+ u_int rate;
+
+ rate = atomic_load_int(&pmc_max_sample_rate);
+ /* Avoid 32-bit overflow. */
+ atomic_store_int(&pmc_max_samples_per_tick,
+ rate == 0 ? 0 : howmany((uint64_t)rate, hz));
+}
+
+static int
+pmc_max_sample_rate_sysctl_handler(SYSCTL_HANDLER_ARGS)
+{
+ u_int rate;
+ int error;
+
+ rate = pmc_max_sample_rate;
+ error = sysctl_handle_int(oidp, &rate, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (rate == 0 && pmc_max_sample_rate != 0)
+ log(LOG_WARNING,
+ "hwpmc: sample rate limit disabled, sampling can stall the system\n");
+ atomic_store_int(&pmc_max_sample_rate, rate);
+ pmc_update_sample_limit();
+ return (0);
+}
+
/*
* Map a row index to a classdep structure and return the adjusted row
* index for the PMC class index.
@@ -2268,15 +2314,7 @@
pmc_process_csw_out(td);
break;
- /*
- * Process accumulated PC samples.
- *
- * This function is expected to be called by hardclock() for
- * each CPU that has accumulated PC samples.
- *
- * This function is to be executed on the CPU whose samples
- * are being processed.
- */
+ /* Process CPU samples. A non-NULL arg means flush only. */
case PMC_FN_DO_SAMPLES:
/*
* Clear the cpu specific bit in the CPU mask before
@@ -2291,9 +2329,24 @@
*/
DPCPU_SET(pmc_sampled, 0);
cpu = PCPU_GET(cpuid);
+ if (arg != NULL) {
+ pmc_process_samples(cpu, PMC_HR);
+ pmc_process_samples(cpu, PMC_SR);
+ pmc_process_samples(cpu, PMC_UR);
+ /* Restart next tick. */
+ if (DPCPU_GET(pmc_throttled) != 0)
+ DPCPU_SET(pmc_sampled, 1);
+ break;
+ }
+ DPCPU_SET(pmc_tick_seq, DPCPU_GET(pmc_tick_seq) + 1);
pmc_process_samples(cpu, PMC_HR);
pmc_process_samples(cpu, PMC_SR);
pmc_process_samples(cpu, PMC_UR);
+ /* Restart even if the ring is empty. */
+ if (DPCPU_GET(pmc_throttled) != 0) {
+ DPCPU_SET(pmc_throttled, 0);
+ pmc_restart_stalled(cpu);
+ }
break;
case PMC_FN_MMAP:
@@ -2920,9 +2973,7 @@
PMCDBG2(PMC,REL,2, "decfg cpu=%d ri=%d", cpu, ri);
- critical_enter();
(void)pcd->pcd_config_pmc(cpu, adjri, NULL);
- critical_exit();
/* adjust the global and process count of SS mode PMCs */
if (mode == PMC_MODE_SS && pm->pm_state == PMC_STATE_RUNNING) {
@@ -4941,7 +4992,7 @@
ps->ps_flags = inuserspace ? PMC_CC_F_USERSPACE : 0;
ps->ps_nsamples_actual = 0;
- callchaindepth = (pm->pm_flags & PMC_F_CALLCHAIN) ?
+ callchaindepth = (pm->pm_flags & PMC_F_CALLCHAIN) != 0 ?
pmc_callchaindepth : 1;
MPASS(ps->ps_pc != NULL);
@@ -5003,7 +5054,28 @@
pmc_process_interrupt_mp(int ring, struct pmc *pm, struct trapframe *tf,
struct pmc_multipart *mp)
{
+ struct pmc_pcpu_state *pps;
struct thread *td;
+ u_int limit;
+ int seq;
+
+ /* Stop and drop above the tick limit. */
+ limit = atomic_load_int(&pmc_max_samples_per_tick);
+ if (limit != 0) {
+ pps = &pm->pm_pcpu_state[curcpu];
+ seq = DPCPU_GET(pmc_tick_seq);
+ if (pps->pps_intr_tick != seq) {
+ pps->pps_intr_tick = seq;
+ pps->pps_intr_count = 0;
+ }
+ if (++pps->pps_intr_count > limit) {
+ DPCPU_SET(pmc_throttled, 1);
+ pps->pps_stalled = 1;
+ counter_u64_add(pmc_stats.pm_intr_throttled, 1);
+ DPCPU_SET(pmc_sampled, 1);
+ return (EBUSY);
+ }
+ }
td = curthread;
if ((pm->pm_flags & PMC_F_USERCALLCHAIN) &&
@@ -5143,10 +5215,9 @@
struct thread *td;
struct pmc_owner *po;
struct pmc_sample *ps;
- struct pmc_classdep *pcd;
struct pmc_samplebuffer *psb;
uint64_t delta __diagused;
- int adjri, n;
+ int n;
KASSERT(PCPU_GET(cpuid) == cpu,
("[pmc,%d] not on the correct CPU pcpu=%d cpu=%d", __LINE__,
@@ -5236,13 +5307,32 @@
if (n == 0)
return;
- /*
- * Restart any stalled sampling PMCs on this CPU.
- *
- * If the NMI handler sets the pm_stalled field of a PMC after
- * the check below, we'll end up processing the stalled PMC at
- * the next hardclock tick.
- */
+ pmc_restart_stalled(cpu);
+}
+
+
+/* Check the tick limit. */
+static bool
+pmc_over_limit(struct pmc *pm, int cpu)
+{
+ struct pmc_pcpu_state *pps;
+ u_int limit;
+
+ limit = atomic_load_int(&pmc_max_samples_per_tick);
+ pps = &pm->pm_pcpu_state[cpu];
+ return (limit != 0 &&
+ pps->pps_intr_tick == DPCPU_ID_GET(cpu, pmc_tick_seq) &&
+ pps->pps_intr_count > limit);
+}
+
+/* Restart stalled PMCs only below the tick limit. */
+static void
+pmc_restart_stalled(int cpu)
+{
+ struct pmc *pm;
+ struct pmc_classdep *pcd;
+ int adjri, n;
+
for (n = 0; n < md->pmd_npmc; n++) {
pcd = pmc_ri_to_classdep(md, n, &adjri);
KASSERT(pcd != NULL,
@@ -5251,11 +5341,19 @@
if (pm == NULL || /* !cfg'ed */
pm->pm_state != PMC_STATE_RUNNING || /* !active */
- !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)) || /* !sampling */
- !pm->pm_pcpu_state[cpu].pps_cpustate || /* !desired */
+ !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) /* !sampling */
+ continue;
+
+ if (!pm->pm_pcpu_state[cpu].pps_cpustate || /* !desired */
!pm->pm_pcpu_state[cpu].pps_stalled) /* !stalled */
continue;
+ if (pmc_over_limit(pm, cpu)) {
+ DPCPU_ID_SET(cpu, pmc_throttled, 1);
+ DPCPU_ID_SET(cpu, pmc_sampled, 1);
+ continue;
+ }
+
pm->pm_pcpu_state[cpu].pps_stalled = 0;
(void)(*pcd->pcd_start_pmc)(cpu, adjri, pm);
}
@@ -5751,6 +5849,7 @@
pmc_stats.pm_log_sweeps = counter_u64_alloc(M_WAITOK);
pmc_stats.pm_merges = counter_u64_alloc(M_WAITOK);
pmc_stats.pm_overwrites = counter_u64_alloc(M_WAITOK);
+ pmc_stats.pm_intr_throttled = counter_u64_alloc(M_WAITOK);
#ifdef HWPMC_DEBUG
/* parse debug flags first */
@@ -5791,6 +5890,8 @@
}
pmc_sample_mask = pmc_nsamples - 1;
+ pmc_update_sample_limit();
+
if (pmc_callchaindepth <= 0 ||
pmc_callchaindepth > PMC_CALLCHAIN_DEPTH_MAX) {
printf("hwpmc: tunable \"callchaindepth\"=%d out of "
@@ -6163,6 +6264,7 @@
counter_u64_free(pmc_stats.pm_log_sweeps);
counter_u64_free(pmc_stats.pm_merges);
counter_u64_free(pmc_stats.pm_overwrites);
+ counter_u64_free(pmc_stats.pm_intr_throttled);
sx_xunlock(&pmc_sx); /* we are done */
}
diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h
--- a/sys/sys/pmc.h
+++ b/sys/sys/pmc.h
@@ -578,6 +578,7 @@
passes */
counter_u64_t pm_merges; /* merged k+u */
counter_u64_t pm_overwrites; /* UR overwrites */
+ counter_u64_t pm_intr_throttled; /* Rate stops. */
};
#endif
@@ -669,6 +670,7 @@
#define PMC_NLOGBUFFERS_PCPU 32
#define PMC_NLOGBUFFERS_PCPU_MEM_MAX (32 * 1024)
#define PMC_NSAMPLES 256
+#define PMC_MAX_SAMPLE_RATE 100000
#define PMC_CALLCHAIN_DEPTH 128
#define PMC_THREADLIST_MAX 128
@@ -747,6 +749,8 @@
*/
struct pmc_pcpu_state {
uint32_t pps_overflowcnt; /* count overflow interrupts */
+ int pps_intr_tick; /* Tick number. */
+ u_int pps_intr_count; /* Interrupts per tick. */
uint8_t pps_stalled;
uint8_t pps_cpustate;
} __aligned(CACHE_LINE_SIZE);
diff --git a/tests/sys/pmc/Makefile b/tests/sys/pmc/Makefile
--- a/tests/sys/pmc/Makefile
+++ b/tests/sys/pmc/Makefile
@@ -10,12 +10,15 @@
.endif
ATF_TESTS_C+= pmc_lifecycle_test
ATF_TESTS_C+= pmc_log_test
+ATF_TESTS_C+= pmc_throttle_test
ATF_TESTS_C+= pmc_wrap_test
TEST_METADATA.pmc_exterr_test+= required_user="root"
+TEST_METADATA.pmc_throttle_test+= is_exclusive="true"
LIBADD+= pmc
LIBADD.pmc_detach_test+= pthread
+LIBADD.pmc_throttle_test+= pthread
CFLAGS.pmc_exterr_test.c+= -I${SRCTOP}/tests
diff --git a/tests/sys/pmc/pmc_throttle_test.c b/tests/sys/pmc/pmc_throttle_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/pmc/pmc_throttle_test.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2026 Advanced Micro Devices, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/* PMC throttle tests. */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TEST_CPU 1
+#define TEST_PERIOD 2000
+#define TEST_SECONDS 2
+
+static const char *cycle_events[] = {
+ "ls_not_halted_cyc", /* AMD Zen */
+ "unhalted-cycles", /* Generic alias */
+ "cpu_clk_unhalted.thread_p", /* Intel */
+};
+
+static atomic_bool spin_stop;
+static atomic_bool flush_stop;
+static atomic_ulong nflush;
+static bool use_flush;
+static int test_period = TEST_PERIOD;
+static const char *test_event; /* NULL: cycle event. */
+static uint32_t test_flags;
+static double test_secs;
+
+static void
+require_hwpmc(void)
+{
+
+ if (pmc_init() != 0)
+ atf_tc_skip("hwpmc(4) is not available");
+ if (sysconf(_SC_NPROCESSORS_ONLN) <= TEST_CPU)
+ atf_tc_skip("needs at least %d CPUs", TEST_CPU + 1);
+}
+
+static void
+pin(int cpu)
+{
+ cpuset_t set;
+
+ CPU_ZERO(&set);
+ CPU_SET(cpu, &set);
+ ATF_REQUIRE(cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1,
+ sizeof(set), &set) == 0);
+}
+
+static void *
+spin_main(void *arg __unused)
+{
+ volatile uint64_t x;
+
+ pin(TEST_CPU);
+ x = 1;
+ while (!atomic_load(&spin_stop))
+ x = x * 6364136223846793005ULL + 1;
+ return (NULL);
+}
+
+static void *
+flush_main(void *arg __unused)
+{
+
+ while (!atomic_load(&flush_stop)) {
+ if (pmc_flush_logfile() == 0)
+ atomic_fetch_add(&nflush, 1);
+ }
+ return (NULL);
+}
+
+static uint64_t
+stat_get(const char *name)
+{
+ char full[128];
+ uint64_t v;
+ size_t len;
+
+ snprintf(full, sizeof(full), "kern.hwpmc.stats.%s", name);
+ len = sizeof(v);
+ ATF_REQUIRE_MSG(sysctlbyname(full, &v, &len, NULL, 0) == 0,
+ "sysctl %s: %s", full, strerror(errno));
+ return (v);
+}
+
+/* Read a PMC sysctl or skip. */
+static u_int
+hwpmc_get(const char *name)
+{
+ char full[64];
+ size_t len;
+ u_int v;
+
+ snprintf(full, sizeof(full), "kern.hwpmc.%s", name);
+ len = sizeof(v);
+ if (sysctlbyname(full, &v, &len, NULL, 0) != 0)
+ atf_tc_skip("%s is not available", full);
+ return (v);
+}
+
+static int
+hwpmc_write(const char *name, u_int v)
+{
+ char full[64];
+
+ snprintf(full, sizeof(full), "kern.hwpmc.%s", name);
+ return (sysctlbyname(full, NULL, NULL, &v, sizeof(v)));
+}
+
+static void
+hwpmc_set(const char *name, u_int v)
+{
+
+ ATF_REQUIRE_MSG(hwpmc_write(name, v) == 0, "set kern.hwpmc.%s: %s",
+ name, strerror(errno));
+}
+
+static int
+hz_get(void)
+{
+ struct clockinfo ci;
+ size_t len;
+
+ len = sizeof(ci);
+ ATF_REQUIRE(sysctlbyname("kern.clockrate", &ci, &len, NULL, 0) == 0);
+ return (ci.hz);
+}
+
+/* Save sysctls for the ATF cleanup process. */
+#define SAVE_FILE "sysctl.saved"
+
+struct saved_sysctls {
+ u_int rate;
+ u_int mincount;
+};
+
+static void
+sysctls_save(void)
+{
+ struct saved_sysctls sv;
+ int fd;
+
+ sv.mincount = hwpmc_get("mincount");
+ sv.rate = hwpmc_get("max_sample_rate");
+ fd = open(SAVE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(write(fd, &sv, sizeof(sv)) == sizeof(sv));
+ close(fd);
+}
+
+static void
+sysctls_restore(void)
+{
+ struct saved_sysctls sv;
+ int fd;
+
+ fd = open(SAVE_FILE, O_RDONLY);
+ if (fd < 0)
+ return;
+ if (read(fd, &sv, sizeof(sv)) == sizeof(sv)) {
+ (void)hwpmc_write("max_sample_rate", sv.rate);
+ (void)hwpmc_write("mincount", sv.mincount);
+ }
+ close(fd);
+ (void)unlink(SAVE_FILE);
+}
+
+/* Set mincount and the tick limit. */
+static int
+throttle_setup(u_int rate_per_tick)
+{
+ int hz;
+
+ require_hwpmc();
+ sysctls_save();
+ hz = hz_get();
+ hwpmc_set("mincount", 1);
+ hwpmc_set("max_sample_rate", rate_per_tick * hz);
+ return (hz);
+}
+
+/* Measure the rate and throttle count on TEST_CPU. */
+static double
+measure_log(uint64_t *throttled, const char *logpath, pmc_id_t *idp)
+{
+ struct timeval t0, t1;
+ pthread_t fthr, thr;
+ pmc_id_t id;
+ uint64_t p0, p1, th0, th1;
+ double secs;
+ u_int i;
+ int fd;
+ bool ok;
+
+ ok = false;
+ if (test_event != NULL) {
+ ok = pmc_allocate(test_event, PMC_MODE_SS, test_flags,
+ TEST_CPU, &id, test_period) == 0;
+ if (!ok)
+ atf_tc_skip("no %s event", test_event);
+ }
+ for (i = 0; !ok && i < nitems(cycle_events); i++) {
+ if (pmc_allocate(cycle_events[i], PMC_MODE_SS, test_flags,
+ TEST_CPU, &id, test_period) == 0)
+ ok = true;
+ }
+ if (!ok)
+ atf_tc_skip("no cycle event for a system sampling PMC");
+
+ if (logpath != NULL)
+ fd = open(logpath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ else
+ fd = open("/dev/null", O_WRONLY);
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(pmc_configure_logfile(fd) == 0);
+ if (idp != NULL)
+ *idp = id;
+
+ atomic_store(&spin_stop, false);
+ ATF_REQUIRE(pthread_create(&thr, NULL, spin_main, NULL) == 0);
+ usleep(100000);
+ atomic_store(&flush_stop, false);
+ atomic_store(&nflush, 0);
+ if (use_flush)
+ ATF_REQUIRE(pthread_create(&fthr, NULL, flush_main, NULL) == 0);
+
+ p0 = stat_get("intr_processed");
+ th0 = stat_get("intr_throttled");
+ gettimeofday(&t0, NULL);
+ ATF_REQUIRE(pmc_start(id) == 0);
+ sleep(TEST_SECONDS);
+ ATF_REQUIRE(pmc_stop(id) == 0);
+ gettimeofday(&t1, NULL);
+ p1 = stat_get("intr_processed");
+ th1 = stat_get("intr_throttled");
+
+ atomic_store(&spin_stop, true);
+ pthread_join(thr, NULL);
+ if (use_flush) {
+ atomic_store(&flush_stop, true);
+ pthread_join(fthr, NULL);
+ }
+ ATF_REQUIRE(pmc_release(id) == 0);
+ if (logpath != NULL)
+ ATF_REQUIRE(pmc_close_logfile() == 0);
+ (void)pmc_configure_logfile(-1);
+ close(fd);
+
+ secs = (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec) / 1e6;
+ test_secs = secs;
+ *throttled = th1 - th0;
+ return ((p1 - p0) / secs);
+}
+
+static double
+measure(uint64_t *throttled)
+{
+
+ return (measure_log(throttled, NULL, NULL));
+}
+
+/* Allow stop interrupts and other PMCs. */
+ATF_TC_WITH_CLEANUP(rate_limited);
+ATF_TC_HEAD(rate_limited, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(rate_limited, tc)
+{
+ uint64_t throttled;
+ double rate, bound;
+ int hz;
+
+ hz = throttle_setup(10);
+
+ rate = measure(&throttled);
+
+ sysctls_restore();
+
+ bound = 1.5 * 11 * hz;
+ printf("hz %d, rate %.0f/s, bound %.0f/s, throttled %ju\n", hz, rate,
+ bound, (uintmax_t)throttled);
+ ATF_CHECK_MSG(throttled > 0, "the PMC never reached the limit");
+ ATF_CHECK_MSG(rate <= bound, "rate %.0f/s is above %.0f/s", rate,
+ bound);
+}
+
+ATF_TC_CLEANUP(rate_limited, tc)
+{
+
+ sysctls_restore();
+}
+
+/* No limit must give a higher rate. */
+ATF_TC_WITH_CLEANUP(rate_unlimited);
+ATF_TC_HEAD(rate_unlimited, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(rate_unlimited, tc)
+{
+ uint64_t throttled;
+ double rate, bound;
+ int hz;
+
+ hz = throttle_setup(0);
+
+ rate = measure(&throttled);
+
+ sysctls_restore();
+
+ bound = 1.5 * 11 * hz;
+ printf("hz %d, rate %.0f/s, throttled %ju\n", hz, rate,
+ (uintmax_t)throttled);
+ ATF_CHECK_EQ_MSG(throttled, 0, "throttled %ju times",
+ (uintmax_t)throttled);
+ ATF_CHECK_MSG(rate > bound, "rate %.0f/s is not above %.0f/s", rate,
+ bound);
+}
+
+ATF_TC_CLEANUP(rate_unlimited, tc)
+{
+
+ sysctls_restore();
+}
+
+/* One throttle per tick. */
+ATF_TC_WITH_CLEANUP(short_period);
+ATF_TC_HEAD(short_period, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(short_period, tc)
+{
+ uint64_t throttled;
+ double per_tick, rate;
+ int hz;
+
+ hz = throttle_setup(100);
+ test_period = 100;
+
+ rate = measure(&throttled);
+
+ sysctls_restore();
+
+ per_tick = throttled / test_secs / hz;
+ printf("hz %d, rate %.0f/s, throttled %ju, %.2f each tick\n", hz,
+ rate, (uintmax_t)throttled, per_tick);
+ ATF_CHECK_MSG(throttled > 0, "the PMC never reached the limit");
+ ATF_CHECK_MSG(per_tick < 1.5, "%.2f throttles in each tick",
+ per_tick);
+}
+
+ATF_TC_CLEANUP(short_period, tc)
+{
+
+ sysctls_restore();
+}
+
+/* Flush must not reset the tick. */
+ATF_TC_WITH_CLEANUP(flush_loop);
+ATF_TC_HEAD(flush_loop, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(flush_loop, tc)
+{
+ uint64_t throttled;
+ double rate, bound;
+ int hz;
+
+ hz = throttle_setup(10);
+ use_flush = true;
+
+ rate = measure(&throttled);
+
+ sysctls_restore();
+
+ bound = 1.5 * 11 * hz;
+ printf("hz %d, rate %.0f/s, bound %.0f/s, flushes %lu, "
+ "throttled %ju\n", hz, rate, bound, atomic_load(&nflush),
+ (uintmax_t)throttled);
+ ATF_CHECK_MSG(atomic_load(&nflush) > 0, "no flush succeeded");
+ ATF_CHECK_MSG(rate <= bound, "rate %.0f/s is above %.0f/s", rate,
+ bound);
+}
+
+ATF_TC_CLEANUP(flush_loop, tc)
+{
+
+ sysctls_restore();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, rate_limited);
+ ATF_TP_ADD_TC(tp, rate_unlimited);
+ ATF_TP_ADD_TC(tp, short_period);
+ ATF_TP_ADD_TC(tp, flush_loop);
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 11, 12:14 PM (8 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41652417
Default Alt Text
D61371.1794399275.diff (21 KB)
Attached To
Mode
D61371: hwpmc: limit PMC sample rates
Attached
Detach File
Event Timeline