xref: /dpdk/lib/jobstats/rte_jobstats.c (revision 30a1de105a5f40d77b344a891c4a68f79e815c43)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2015 Intel Corporation
3  */
4 
5 #include <string.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 
9 #include <rte_string_fns.h>
10 #include <rte_common.h>
11 #include <rte_cycles.h>
12 #include <rte_branch_prediction.h>
13 
14 #include "rte_jobstats.h"
15 
16 #define ADD_TIME_MIN_MAX(obj, type, value) do {      \
17 	typeof(value) tmp = (value);                     \
18 	(obj)->type ## _time += tmp;                     \
19 	if (tmp < (obj)->min_ ## type ## _time)          \
20 		(obj)->min_ ## type ## _time = tmp;          \
21 	if (tmp > (obj)->max_ ## type ## _time)          \
22 		(obj)->max_ ## type ## _time = tmp;          \
23 } while (0)
24 
25 #define RESET_TIME_MIN_MAX(obj, type) do {           \
26 	(obj)->type ## _time = 0;                        \
27 	(obj)->min_ ## type ## _time = UINT64_MAX;       \
28 	(obj)->max_ ## type ## _time = 0;                \
29 } while (0)
30 
31 static inline uint64_t
get_time(void)32 get_time(void)
33 {
34 	rte_rmb();
35 	return rte_get_timer_cycles();
36 }
37 
38 /* Those are steps used to adjust job period.
39  * Experiments show that for forwarding apps the up step must be less than down
40  * step to achieve optimal performance.
41  */
42 #define JOB_UPDATE_STEP_UP    1
43 #define JOB_UPDATE_STEP_DOWN  4
44 
45 /*
46  * Default update function that implements simple period adjustment.
47  */
48 static void
default_update_function(struct rte_jobstats * job,int64_t result)49 default_update_function(struct rte_jobstats *job, int64_t result)
50 {
51 	int64_t err = job->target - result;
52 
53 	/* Job is happy. Nothing to do */
54 	if (err == 0)
55 		return;
56 
57 	if (err > 0) {
58 		if (job->period + JOB_UPDATE_STEP_UP < job->max_period)
59 			job->period += JOB_UPDATE_STEP_UP;
60 	} else {
61 		if (job->min_period + JOB_UPDATE_STEP_DOWN < job->period)
62 			job->period -= JOB_UPDATE_STEP_DOWN;
63 	}
64 }
65 
66 int
rte_jobstats_context_init(struct rte_jobstats_context * ctx)67 rte_jobstats_context_init(struct rte_jobstats_context *ctx)
68 {
69 	if (ctx == NULL)
70 		return -EINVAL;
71 
72 	/* Init only needed parameters. Zero out everything else. */
73 	memset(ctx, 0, sizeof(struct rte_jobstats_context));
74 
75 	rte_jobstats_context_reset(ctx);
76 
77 	return 0;
78 }
79 
80 void
rte_jobstats_context_start(struct rte_jobstats_context * ctx)81 rte_jobstats_context_start(struct rte_jobstats_context *ctx)
82 {
83 	uint64_t now;
84 
85 	ctx->loop_executed_jobs = 0;
86 
87 	now = get_time();
88 	ADD_TIME_MIN_MAX(ctx, management, now - ctx->state_time);
89 	ctx->state_time = now;
90 }
91 
92 void
rte_jobstats_context_finish(struct rte_jobstats_context * ctx)93 rte_jobstats_context_finish(struct rte_jobstats_context *ctx)
94 {
95 	uint64_t now;
96 
97 	if (likely(ctx->loop_executed_jobs))
98 		ctx->loop_cnt++;
99 
100 	now = get_time();
101 	ADD_TIME_MIN_MAX(ctx, management, now - ctx->state_time);
102 	ctx->state_time = now;
103 }
104 
105 void
rte_jobstats_context_reset(struct rte_jobstats_context * ctx)106 rte_jobstats_context_reset(struct rte_jobstats_context *ctx)
107 {
108 	RESET_TIME_MIN_MAX(ctx, exec);
109 	RESET_TIME_MIN_MAX(ctx, management);
110 	ctx->start_time = get_time();
111 	ctx->state_time = ctx->start_time;
112 	ctx->job_exec_cnt = 0;
113 	ctx->loop_cnt = 0;
114 }
115 
116 void
rte_jobstats_set_target(struct rte_jobstats * job,int64_t target)117 rte_jobstats_set_target(struct rte_jobstats *job, int64_t target)
118 {
119 	job->target = target;
120 }
121 
122 int
rte_jobstats_start(struct rte_jobstats_context * ctx,struct rte_jobstats * job)123 rte_jobstats_start(struct rte_jobstats_context *ctx, struct rte_jobstats *job)
124 {
125 	uint64_t now;
126 
127 	/* Some sanity check. */
128 	if (unlikely(ctx == NULL || job == NULL || job->context != NULL))
129 		return -EINVAL;
130 
131 	/* Link job with context object. */
132 	job->context = ctx;
133 
134 	now = get_time();
135 	ADD_TIME_MIN_MAX(ctx, management, now - ctx->state_time);
136 	ctx->state_time = now;
137 
138 	return 0;
139 }
140 
141 int
rte_jobstats_abort(struct rte_jobstats * job)142 rte_jobstats_abort(struct rte_jobstats *job)
143 {
144 	struct rte_jobstats_context *ctx;
145 	uint64_t now, exec_time;
146 
147 	/* Some sanity check. */
148 	if (unlikely(job == NULL || job->context == NULL))
149 		return -EINVAL;
150 
151 	ctx = job->context;
152 	now = get_time();
153 	exec_time = now - ctx->state_time;
154 	ADD_TIME_MIN_MAX(ctx, management, exec_time);
155 	ctx->state_time = now;
156 	job->context = NULL;
157 
158 	return 0;
159 }
160 
161 int
rte_jobstats_finish(struct rte_jobstats * job,int64_t job_value)162 rte_jobstats_finish(struct rte_jobstats *job, int64_t job_value)
163 {
164 	struct rte_jobstats_context *ctx;
165 	uint64_t now, exec_time;
166 	int need_update;
167 
168 	/* Some sanity check. */
169 	if (unlikely(job == NULL || job->context == NULL))
170 		return -EINVAL;
171 
172 	need_update = job->target != job_value;
173 	/* Adjust period only if job is unhappy of its current period. */
174 	if (need_update)
175 		(*job->update_period_cb)(job, job_value);
176 
177 	ctx = job->context;
178 
179 	/* Update execution time is considered as runtime so get time after it is
180 	 * executed. */
181 	now = get_time();
182 	exec_time = now - ctx->state_time;
183 	ADD_TIME_MIN_MAX(job, exec, exec_time);
184 	ADD_TIME_MIN_MAX(ctx, exec, exec_time);
185 
186 	ctx->state_time = now;
187 
188 	ctx->loop_executed_jobs++;
189 	ctx->job_exec_cnt++;
190 
191 	job->exec_cnt++;
192 	job->context = NULL;
193 
194 	return need_update;
195 }
196 
197 void
rte_jobstats_set_period(struct rte_jobstats * job,uint64_t period,uint8_t saturate)198 rte_jobstats_set_period(struct rte_jobstats *job, uint64_t period,
199 		uint8_t saturate)
200 {
201 	if (saturate != 0) {
202 		if (period < job->min_period)
203 			period = job->min_period;
204 		else if (period > job->max_period)
205 			period = job->max_period;
206 	}
207 
208 	job->period = period;
209 }
210 
211 void
rte_jobstats_set_min(struct rte_jobstats * job,uint64_t period)212 rte_jobstats_set_min(struct rte_jobstats *job, uint64_t period)
213 {
214 	job->min_period = period;
215 	if (job->period < period)
216 		job->period = period;
217 }
218 
219 void
rte_jobstats_set_max(struct rte_jobstats * job,uint64_t period)220 rte_jobstats_set_max(struct rte_jobstats *job, uint64_t period)
221 {
222 	job->max_period = period;
223 	if (job->period > period)
224 		job->period = period;
225 }
226 
227 int
rte_jobstats_init(struct rte_jobstats * job,const char * name,uint64_t min_period,uint64_t max_period,uint64_t initial_period,int64_t target)228 rte_jobstats_init(struct rte_jobstats *job, const char *name,
229 		uint64_t min_period, uint64_t max_period, uint64_t initial_period,
230 		int64_t target)
231 {
232 	if (job == NULL)
233 		return -EINVAL;
234 
235 	job->period = initial_period;
236 	job->min_period = min_period;
237 	job->max_period = max_period;
238 	job->target = target;
239 	job->update_period_cb = &default_update_function;
240 	rte_jobstats_reset(job);
241 	strlcpy(job->name, name == NULL ? "" : name, RTE_DIM(job->name));
242 	job->context = NULL;
243 
244 	return 0;
245 }
246 
247 void
rte_jobstats_set_update_period_function(struct rte_jobstats * job,rte_job_update_period_cb_t update_period_cb)248 rte_jobstats_set_update_period_function(struct rte_jobstats *job,
249 		rte_job_update_period_cb_t update_period_cb)
250 {
251 	if (update_period_cb == NULL)
252 		update_period_cb = default_update_function;
253 
254 	job->update_period_cb = update_period_cb;
255 }
256 
257 void
rte_jobstats_reset(struct rte_jobstats * job)258 rte_jobstats_reset(struct rte_jobstats *job)
259 {
260 	RESET_TIME_MIN_MAX(job, exec);
261 	job->exec_cnt = 0;
262 }
263