liyujie
2025-08-28 b3810562527858a3b3d98ffa6e9c9c5b0f4a9a8e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
 * Copyright (c) 2018 Google, Inc.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * This test attempts to verify that the schedutil governor does not take into
 * account stale utilization from an idle CPU when calculating the frequency for
 * a shared policy.
 *
 * This test is not yet complete and may never be. The CPU in question may
 * receive spurious updates which push the stale deadline out, causing the test
 * to fail.
 */
 
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <time.h>
#include <semaphore.h>
#include <stdlib.h>
 
#include "tst_test.h"
#include "tst_safe_file_ops.h"
#include "tst_safe_pthread.h"
 
#include "trace_parse.h"
#include "util.h"
 
#define TRACE_EVENTS "sugov_next_freq sugov_util_update"
 
#define MAX_TEST_CPUS 32
static int policy_cpus[MAX_TEST_CPUS];
static int policy_num_cpus = 0;
 
static int test_cpu;
static sem_t sem;
 
/* sugov currently waits 1.125 * TICK_NSEC, which with HZ=300, is
 * ~3.75ms for PELT
 * On WALT, 1.125 * sched_ravg_window (20ms) is 22.5ms */
#define MAX_STALE_USEC 22500
/* The event task may not wake up right away due to timer slack. */
#define SLACK_USEC 10000
 
static void *event_fn(void *arg LTP_ATTRIBUTE_UNUSED)
{
   /*
    * FIXME: Proper logic to identify a multi-CPU policy and select two
    * CPUS from it is required here.
    */
   affine(test_cpu - 1);
 
   sem_wait(&sem);
 
   SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
            "event task sleep");
   usleep(MAX_STALE_USEC);
   SAFE_FILE_PRINTF(TRACING_DIR "trace_marker",
            "event task wake");
   /*
    * Waking up should be sufficient to get the cpufreq policy to
    * re-evaluate.
    */
   return NULL;
}
 
#define BURN_MSEC 500
static void *burn_fn(void *arg LTP_ATTRIBUTE_UNUSED)
{
   affine(test_cpu);
 
   /*
    * wait a bit to allow any hacks to boost frequency on migration
    * to take effect
    */
   usleep(200);
 
   /* Busy loop for BURN_MSEC to get the task demand to maximum. */
   burn(BURN_MSEC * 1000, 0);
 
   /*
    * Sleep. The next sugov update after TICK_NSEC should not include
    * this task's contribution.
    */
   SAFE_FILE_PRINTF(TRACING_DIR "trace_marker", "sleeping");
 
   /*
    * Wake up task on another CPU in the same policy which will sleep
    * for stale_ns, then wake up briefly to trigger a recalculation of the
    * cpufreq policy.
    */
   sem_post(&sem);
   sleep(2);
 
   return NULL;
}
 
static int cpu_in_policy(int cpu)
{
   int i;
   for (i = 0; i < policy_num_cpus; i++)
       if (cpu == policy_cpus[i])
           return 1;
   return 0;
}
 
static int parse_results(void)
{
   int i, sleep_idx;
   int max_util_seen = 0;
   unsigned int stale_usec;
 
   /* Verify that utilization reached 1024 before sleep. */
   for (i = 0; i < num_trace_records; i++) {
       if (trace[i].event_type == TRACE_RECORD_SUGOV_UTIL_UPDATE) {
           struct trace_sugov_util_update *t =
               trace[i].event_data;
           if (t->cpu == test_cpu && t->util > max_util_seen)
               max_util_seen = t->util;
       }
       if (trace[i].event_type == TRACE_RECORD_TRACING_MARK_WRITE &&
           !strcmp(trace[i].event_data, "sleeping"))
           break;
   }
   printf("Max util seen from CPU hog: %d\n", max_util_seen);
   if (max_util_seen < 1000) {
       printf("Trace parse error, utilization of CPU hog did "
              "not reach 1000.\n");
       return -1;
   }
   sleep_idx = i;
//    print_trace_record(&trace[i]);
   for (; i < num_trace_records; i++)
       if (trace[i].event_type == TRACE_RECORD_SUGOV_NEXT_FREQ) {
           struct trace_sugov_next_freq *t =
               trace[i].event_data;
           /* We should only see some minor utilization. */
           if (cpu_in_policy(t->cpu) && t->util < 200)
               break;
       }
   if (i == num_trace_records) {
       printf("Trace parse error, util never went stale!\n");
       return -1;
   }
//    print_trace_record(&trace[i]);
   stale_usec = TS_TO_USEC(trace[i].ts) - TS_TO_USEC(trace[sleep_idx].ts);
 
   printf("Stale vote shown to be cleared in %d usec.\n", stale_usec);
   return (stale_usec > (MAX_STALE_USEC + SLACK_USEC));
}
 
#define POLICY_CPUS_BUFSIZE 1024
static void get_policy_cpus(void)
{
   int i=0, len, policy_cpus_fd;
   char policy_cpus_fname[128];;
   char *buf;
 
   sprintf(policy_cpus_fname,
       "/sys/devices/system/cpu/cpu%d/cpufreq/related_cpus",
       test_cpu);
   buf = SAFE_MALLOC(POLICY_CPUS_BUFSIZE);
 
   policy_cpus_fd = open(policy_cpus_fname, O_RDONLY);
   if (policy_cpus_fd < 0) {
       printf("Failed to open policy cpus (errno %d)\n",
              errno);
       return;
   }
 
   len = read(policy_cpus_fd, buf, POLICY_CPUS_BUFSIZE -1);
   /* At least one digit is expected. */
   if (len < 2) {
       printf("Read of policy cpus returned %d (errno %d)\n",
              len, errno);
       return;
   }
   close(policy_cpus_fd);
   /* buf now has a list of CPUs, parse it */
   while(buf[i] >= '0' && buf[i] <= '9') {
       int j = i;
       while (buf[j] >= '0' && buf[j] <= '9')
           j++;
       buf[j] = 0;
       policy_cpus[policy_num_cpus++] = atoi(&buf[i]);
       i = j + 1;
   }
   printf("Testing on CPU %d, all CPUs in that policy:\n",
          test_cpu);
   for (int i = 0; i < policy_num_cpus; i++)
       printf(" %d", policy_cpus[i]);
   printf("\n");
   free(buf);
}
 
static void run(void)
{
   pthread_t burn_thread, event_thread;
 
   test_cpu = tst_ncpus() - 1;
   printf("CPU hog will be bound to CPU %d.\n", test_cpu);
   get_policy_cpus();
 
   sem_init(&sem, 0, 0);
 
   /* configure and enable tracing */
   SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
   SAFE_FILE_PRINTF(TRACING_DIR "buffer_size_kb", "16384");
   SAFE_FILE_PRINTF(TRACING_DIR "set_event", TRACE_EVENTS);
   SAFE_FILE_PRINTF(TRACING_DIR "trace", "\n");
   SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "1");
 
   SAFE_PTHREAD_CREATE(&burn_thread, NULL, burn_fn, NULL);
   SAFE_PTHREAD_CREATE(&event_thread, NULL, event_fn, NULL);
 
   SAFE_PTHREAD_JOIN(burn_thread, NULL);
   SAFE_PTHREAD_JOIN(event_thread, NULL);
 
   /* disable tracing */
   SAFE_FILE_PRINTF(TRACING_DIR "tracing_on", "0");
   LOAD_TRACE();
 
   if (parse_results())
       tst_res(TFAIL, "Stale utilization not cleared within expected "
           "time (%d usec).\n", MAX_STALE_USEC + SLACK_USEC);
   else
       tst_res(TPASS, "Stale utilization cleared within expected "
           "time.\n");
}
 
static struct tst_test test = {
   .test_all = run,
   .cleanup = trace_cleanup,
};