Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2020 Intel Corporation
3 : : */
4 : :
5 : : #include <stdlib.h>
6 : :
7 : : #include <rte_lcore.h>
8 : : #include <rte_lcore_var.h>
9 : : #include <rte_cycles.h>
10 : : #include <rte_cpuflags.h>
11 : : #include <rte_malloc.h>
12 : : #include <rte_ethdev.h>
13 : : #include <rte_power_intrinsics.h>
14 : :
15 : : #include "rte_power_pmd_mgmt.h"
16 : : #include "power_common.h"
17 : :
18 : : unsigned int emptypoll_max;
19 : : unsigned int pause_duration;
20 : : unsigned int scale_freq_min[RTE_MAX_LCORE];
21 : : unsigned int scale_freq_max[RTE_MAX_LCORE];
22 : :
23 : : /* store some internal state */
24 : : static struct pmd_conf_data {
25 : : /** what do we support? */
26 : : struct rte_cpu_intrinsics intrinsics_support;
27 : : /** pre-calculated tsc diff for 1us */
28 : : uint64_t tsc_per_us;
29 : : /** how many rte_pause can we fit in a microsecond? */
30 : : uint64_t pause_per_us;
31 : : } global_data;
32 : :
33 : : /**
34 : : * Possible power management states of an ethdev port.
35 : : */
36 : : enum pmd_mgmt_state {
37 : : /** Device power management is disabled. */
38 : : PMD_MGMT_DISABLED = 0,
39 : : /** Device power management is enabled. */
40 : : PMD_MGMT_ENABLED
41 : : };
42 : :
43 : : union queue {
44 : : uint32_t val;
45 : : struct {
46 : : uint16_t portid;
47 : : uint16_t qid;
48 : : };
49 : : };
50 : :
51 : : struct queue_list_entry {
52 : : TAILQ_ENTRY(queue_list_entry) next;
53 : : union queue queue;
54 : : uint64_t n_empty_polls;
55 : : uint64_t n_sleeps;
56 : : const struct rte_eth_rxtx_callback *cb;
57 : : };
58 : :
59 : : struct pmd_core_cfg {
60 : : TAILQ_HEAD(queue_list_head, queue_list_entry) head;
61 : : /**< List of queues associated with this lcore */
62 : : size_t n_queues;
63 : : /**< How many queues are in the list? */
64 : : volatile enum pmd_mgmt_state pwr_mgmt_state;
65 : : /**< State of power management for this queue */
66 : : enum rte_power_pmd_mgmt_type cb_mode;
67 : : /**< Callback mode for this queue */
68 : : uint64_t n_queues_ready_to_sleep;
69 : : /**< Number of queues ready to enter power optimized state */
70 : : uint64_t sleep_target;
71 : : /**< Prevent a queue from triggering sleep multiple times */
72 : : };
73 : : static RTE_LCORE_VAR_HANDLE(struct pmd_core_cfg, lcore_cfgs);
74 : :
75 : : static void
76 : 0 : init_lcore_cfgs(void)
77 : : {
78 : : struct pmd_core_cfg *lcore_cfg;
79 : : unsigned int lcore_id;
80 : :
81 [ # # ]: 0 : if (lcore_cfgs != NULL)
82 : : return;
83 : :
84 : 0 : RTE_LCORE_VAR_ALLOC(lcore_cfgs);
85 : :
86 : : /* initialize all tailqs */
87 [ # # ]: 0 : RTE_LCORE_VAR_FOREACH(lcore_id, lcore_cfg, lcore_cfgs)
88 : 0 : TAILQ_INIT(&lcore_cfg->head);
89 : : }
90 : :
91 : : static inline bool
92 : : queue_equal(const union queue *l, const union queue *r)
93 : : {
94 : 0 : return l->val == r->val;
95 : : }
96 : :
97 : : static inline void
98 : : queue_copy(union queue *dst, const union queue *src)
99 : : {
100 : 0 : dst->val = src->val;
101 : : }
102 : :
103 : : static struct queue_list_entry *
104 : : queue_list_find(const struct pmd_core_cfg *cfg, const union queue *q)
105 : : {
106 : : struct queue_list_entry *cur;
107 : :
108 [ # # # # ]: 0 : TAILQ_FOREACH(cur, &cfg->head, next) {
109 [ # # # # ]: 0 : if (queue_equal(&cur->queue, q))
110 : : return cur;
111 : : }
112 : : return NULL;
113 : : }
114 : :
115 : : static int
116 : 0 : queue_list_add(struct pmd_core_cfg *cfg, const union queue *q)
117 : : {
118 : : struct queue_list_entry *qle;
119 : :
120 : : /* is it already in the list? */
121 [ # # ]: 0 : if (queue_list_find(cfg, q) != NULL)
122 : : return -EEXIST;
123 : :
124 : 0 : qle = malloc(sizeof(*qle));
125 [ # # ]: 0 : if (qle == NULL)
126 : : return -ENOMEM;
127 : : memset(qle, 0, sizeof(*qle));
128 : :
129 : : queue_copy(&qle->queue, q);
130 : 0 : TAILQ_INSERT_TAIL(&cfg->head, qle, next);
131 : 0 : cfg->n_queues++;
132 : :
133 : 0 : return 0;
134 : : }
135 : :
136 : : static struct queue_list_entry *
137 : 0 : queue_list_take(struct pmd_core_cfg *cfg, const union queue *q)
138 : : {
139 : : struct queue_list_entry *found;
140 : :
141 : : found = queue_list_find(cfg, q);
142 [ # # ]: 0 : if (found == NULL)
143 : : return NULL;
144 : :
145 [ # # ]: 0 : TAILQ_REMOVE(&cfg->head, found, next);
146 : 0 : cfg->n_queues--;
147 : :
148 : : /* freeing is responsibility of the caller */
149 : 0 : return found;
150 : : }
151 : :
152 : : static inline int
153 : 0 : get_monitor_addresses(struct pmd_core_cfg *cfg,
154 : : struct rte_power_monitor_cond *pmc, size_t len)
155 : : {
156 : : const struct queue_list_entry *qle;
157 : : size_t i = 0;
158 : : int ret;
159 : :
160 [ # # ]: 0 : TAILQ_FOREACH(qle, &cfg->head, next) {
161 : : const union queue *q = &qle->queue;
162 : : struct rte_power_monitor_cond *cur;
163 : :
164 : : /* attempted out of bounds access */
165 [ # # ]: 0 : if (i >= len) {
166 : 0 : POWER_LOG(ERR, "Too many queues being monitored");
167 : 0 : return -1;
168 : : }
169 : :
170 : 0 : cur = &pmc[i++];
171 : 0 : ret = rte_eth_get_monitor_addr(q->portid, q->qid, cur);
172 [ # # ]: 0 : if (ret < 0)
173 : 0 : return ret;
174 : : }
175 : : return 0;
176 : : }
177 : :
178 : : static void
179 : 0 : calc_tsc(void)
180 : : {
181 : : const uint64_t hz = rte_get_timer_hz();
182 : 0 : const uint64_t tsc_per_us = hz / US_PER_S; /* 1us */
183 : :
184 : 0 : global_data.tsc_per_us = tsc_per_us;
185 : :
186 : : /* only do this if we don't have tpause */
187 [ # # ]: 0 : if (!global_data.intrinsics_support.power_pause) {
188 : : const uint64_t start = rte_rdtsc_precise();
189 : : const uint32_t n_pauses = 10000;
190 : : double us, us_per_pause;
191 : : uint64_t end;
192 : : unsigned int i;
193 : :
194 : : /* estimate number of rte_pause() calls per us*/
195 [ # # ]: 0 : for (i = 0; i < n_pauses; i++)
196 : : rte_pause();
197 : :
198 : : end = rte_rdtsc_precise();
199 : 0 : us = (end - start) / (double)tsc_per_us;
200 : 0 : us_per_pause = us / n_pauses;
201 : :
202 : 0 : global_data.pause_per_us = (uint64_t)(1.0 / us_per_pause);
203 : : }
204 : 0 : }
205 : :
206 : : static inline void
207 : : queue_reset(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
208 : : {
209 : 0 : const bool is_ready_to_sleep = qcfg->n_sleeps == cfg->sleep_target;
210 : :
211 : : /* reset empty poll counter for this queue */
212 : 0 : qcfg->n_empty_polls = 0;
213 : : /* reset the queue sleep counter as well */
214 : 0 : qcfg->n_sleeps = 0;
215 : : /* remove the queue from list of queues ready to sleep */
216 [ # # # # : 0 : if (is_ready_to_sleep)
# # ]
217 : 0 : cfg->n_queues_ready_to_sleep--;
218 : : /*
219 : : * no need change the lcore sleep target counter because this lcore will
220 : : * reach the n_sleeps anyway, and the other cores are already counted so
221 : : * there's no need to do anything else.
222 : : */
223 : : }
224 : :
225 : : static inline bool
226 : : queue_can_sleep(struct pmd_core_cfg *cfg, struct queue_list_entry *qcfg)
227 : : {
228 : : /* this function is called - that means we have an empty poll */
229 : 0 : qcfg->n_empty_polls++;
230 : :
231 : : /* if we haven't reached threshold for empty polls, we can't sleep */
232 [ # # # # ]: 0 : if (qcfg->n_empty_polls <= emptypoll_max)
233 : : return false;
234 : :
235 : : /*
236 : : * we've reached a point where we are able to sleep, but we still need
237 : : * to check if this queue has already been marked for sleeping.
238 : : */
239 [ # # # # : 0 : if (qcfg->n_sleeps == cfg->sleep_target)
# # ]
240 : : return true;
241 : :
242 : : /* mark this queue as ready for sleep */
243 : 0 : qcfg->n_sleeps = cfg->sleep_target;
244 : 0 : cfg->n_queues_ready_to_sleep++;
245 : :
246 : : return true;
247 : : }
248 : :
249 : : static inline bool
250 : : lcore_can_sleep(struct pmd_core_cfg *cfg)
251 : : {
252 : : /* are all queues ready to sleep? */
253 [ # # # # : 0 : if (cfg->n_queues_ready_to_sleep != cfg->n_queues)
# # ]
254 : : return false;
255 : :
256 : : /* we've reached an iteration where we can sleep, reset sleep counter */
257 : 0 : cfg->n_queues_ready_to_sleep = 0;
258 : 0 : cfg->sleep_target++;
259 : : /*
260 : : * we do not reset any individual queue empty poll counters, because
261 : : * we want to keep sleeping on every poll until we actually get traffic.
262 : : */
263 : :
264 : : return true;
265 : : }
266 : :
267 : : static uint16_t
268 : 0 : clb_multiwait(uint16_t port_id __rte_unused, uint16_t qidx __rte_unused,
269 : : struct rte_mbuf **pkts __rte_unused, uint16_t nb_rx,
270 : : uint16_t max_pkts __rte_unused, void *arg)
271 : : {
272 : : struct queue_list_entry *queue_conf = arg;
273 : : struct pmd_core_cfg *lcore_conf;
274 : : const bool empty = nb_rx == 0;
275 : :
276 [ # # ]: 0 : lcore_conf = RTE_LCORE_VAR(lcore_cfgs);
277 : :
278 : : /* early exit */
279 [ # # ]: 0 : if (likely(!empty))
280 : : /* early exit */
281 : : queue_reset(lcore_conf, queue_conf);
282 : 0 : else {
283 [ # # ]: 0 : struct rte_power_monitor_cond pmc[lcore_conf->n_queues];
284 : : int ret;
285 : :
286 : : /* can this queue sleep? */
287 : : if (!queue_can_sleep(lcore_conf, queue_conf))
288 : 0 : return nb_rx;
289 : :
290 : : /* can this lcore sleep? */
291 : : if (!lcore_can_sleep(lcore_conf))
292 : : return nb_rx;
293 : :
294 : : /* gather all monitoring conditions */
295 : 0 : ret = get_monitor_addresses(lcore_conf, pmc,
296 : : lcore_conf->n_queues);
297 [ # # ]: 0 : if (ret < 0)
298 : : return nb_rx;
299 : :
300 : 0 : rte_power_monitor_multi(pmc, lcore_conf->n_queues, UINT64_MAX);
301 : : }
302 : :
303 : : return nb_rx;
304 : : }
305 : :
306 : : static uint16_t
307 : 0 : clb_umwait(uint16_t port_id, uint16_t qidx, struct rte_mbuf **pkts __rte_unused,
308 : : uint16_t nb_rx, uint16_t max_pkts __rte_unused, void *arg)
309 : : {
310 : : struct queue_list_entry *queue_conf = arg;
311 : :
312 : : /* this callback can't do more than one queue, omit multiqueue logic */
313 [ # # ]: 0 : if (unlikely(nb_rx == 0)) {
314 : 0 : queue_conf->n_empty_polls++;
315 [ # # ]: 0 : if (unlikely(queue_conf->n_empty_polls > emptypoll_max)) {
316 : : struct rte_power_monitor_cond pmc;
317 : : int ret;
318 : :
319 : : /* use monitoring condition to sleep */
320 : 0 : ret = rte_eth_get_monitor_addr(port_id, qidx,
321 : : &pmc);
322 [ # # ]: 0 : if (ret == 0)
323 : 0 : rte_power_monitor(&pmc, UINT64_MAX);
324 : : }
325 : : } else
326 : 0 : queue_conf->n_empty_polls = 0;
327 : :
328 : 0 : return nb_rx;
329 : : }
330 : :
331 : : static uint16_t
332 : 0 : clb_pause(uint16_t port_id __rte_unused, uint16_t qidx __rte_unused,
333 : : struct rte_mbuf **pkts __rte_unused, uint16_t nb_rx,
334 : : uint16_t max_pkts __rte_unused, void *arg)
335 : : {
336 : : struct queue_list_entry *queue_conf = arg;
337 : : struct pmd_core_cfg *lcore_conf;
338 : : const bool empty = nb_rx == 0;
339 : 0 : uint32_t pause_duration = rte_power_pmd_mgmt_get_pause_duration();
340 : :
341 [ # # ]: 0 : lcore_conf = RTE_LCORE_VAR(lcore_cfgs);
342 : :
343 [ # # ]: 0 : if (likely(!empty))
344 : : /* early exit */
345 : : queue_reset(lcore_conf, queue_conf);
346 : : else {
347 : : /* can this queue sleep? */
348 : : if (!queue_can_sleep(lcore_conf, queue_conf))
349 : : return nb_rx;
350 : :
351 : : /* can this lcore sleep? */
352 : : if (!lcore_can_sleep(lcore_conf))
353 : : return nb_rx;
354 : :
355 : : /* sleep for 1 microsecond, use tpause if we have it */
356 [ # # ]: 0 : if (global_data.intrinsics_support.power_pause) {
357 : : const uint64_t cur = rte_rdtsc();
358 : 0 : const uint64_t wait_tsc =
359 : 0 : cur + global_data.tsc_per_us * pause_duration;
360 : 0 : rte_power_pause(wait_tsc);
361 : : } else {
362 : : uint64_t i;
363 [ # # ]: 0 : for (i = 0; i < global_data.pause_per_us * pause_duration; i++)
364 : : rte_pause();
365 : : }
366 : : }
367 : :
368 : : return nb_rx;
369 : : }
370 : :
371 : : static uint16_t
372 : 0 : clb_scale_freq(uint16_t port_id __rte_unused, uint16_t qidx __rte_unused,
373 : : struct rte_mbuf **pkts __rte_unused, uint16_t nb_rx,
374 : : uint16_t max_pkts __rte_unused, void *arg)
375 : : {
376 : : const bool empty = nb_rx == 0;
377 [ # # ]: 0 : struct pmd_core_cfg *lcore_conf = RTE_LCORE_VAR(lcore_cfgs);
378 : : struct queue_list_entry *queue_conf = arg;
379 : :
380 [ # # ]: 0 : if (likely(!empty)) {
381 : : /* early exit */
382 : : queue_reset(lcore_conf, queue_conf);
383 : :
384 : : /* scale up freq immediately */
385 : 0 : rte_power_freq_max(rte_lcore_id());
386 : : } else {
387 : : /* can this queue sleep? */
388 : : if (!queue_can_sleep(lcore_conf, queue_conf))
389 : : return nb_rx;
390 : :
391 : : /* can this lcore sleep? */
392 : : if (!lcore_can_sleep(lcore_conf))
393 : : return nb_rx;
394 : :
395 : 0 : rte_power_freq_min(rte_lcore_id());
396 : : }
397 : :
398 : : return nb_rx;
399 : : }
400 : :
401 : : static int
402 : : queue_stopped(const uint16_t port_id, const uint16_t queue_id)
403 : : {
404 : : struct rte_eth_rxq_info qinfo;
405 : :
406 : 0 : int ret = rte_eth_rx_queue_info_get(port_id, queue_id, &qinfo);
407 [ # # # # : 0 : if (ret < 0) {
# # ]
408 [ # # # # : 0 : if (ret == -ENOTSUP)
# # ]
409 : : return 1;
410 : : else
411 : : return -1;
412 : : }
413 : :
414 : 0 : return qinfo.queue_state == RTE_ETH_QUEUE_STATE_STOPPED;
415 : : }
416 : :
417 : : static int
418 : 0 : cfg_queues_stopped(struct pmd_core_cfg *queue_cfg)
419 : : {
420 : : const struct queue_list_entry *entry;
421 : :
422 [ # # ]: 0 : TAILQ_FOREACH(entry, &queue_cfg->head, next) {
423 : : const union queue *q = &entry->queue;
424 : 0 : int ret = queue_stopped(q->portid, q->qid);
425 [ # # ]: 0 : if (ret != 1)
426 : 0 : return ret;
427 : : }
428 : : return 1;
429 : : }
430 : :
431 : : static int
432 : 0 : check_scale(unsigned int lcore)
433 : : {
434 : : enum power_management_env env;
435 : :
436 : : /* only PSTATE, AMD-PSTATE, ACPI and CPPC modes are supported */
437 [ # # # # ]: 0 : if (!rte_power_check_env_supported(PM_ENV_ACPI_CPUFREQ) &&
438 [ # # ]: 0 : !rte_power_check_env_supported(PM_ENV_PSTATE_CPUFREQ) &&
439 [ # # ]: 0 : !rte_power_check_env_supported(PM_ENV_AMD_PSTATE_CPUFREQ) &&
440 : 0 : !rte_power_check_env_supported(PM_ENV_CPPC_CPUFREQ)) {
441 : 0 : POWER_LOG(DEBUG, "Only ACPI, PSTATE, AMD-PSTATE, or CPPC modes are supported");
442 : 0 : return -ENOTSUP;
443 : : }
444 : : /* ensure we could initialize the power library */
445 [ # # ]: 0 : if (rte_power_init(lcore))
446 : : return -EINVAL;
447 : :
448 : : /* ensure we initialized the correct env */
449 : 0 : env = rte_power_get_env();
450 [ # # ]: 0 : if (env != PM_ENV_ACPI_CPUFREQ && env != PM_ENV_PSTATE_CPUFREQ &&
451 [ # # ]: 0 : env != PM_ENV_AMD_PSTATE_CPUFREQ && env != PM_ENV_CPPC_CPUFREQ) {
452 : 0 : POWER_LOG(DEBUG, "Unable to initialize ACPI, PSTATE, AMD-PSTATE, or CPPC modes");
453 : 0 : return -ENOTSUP;
454 : : }
455 : :
456 : : /* we're done */
457 : : return 0;
458 : : }
459 : :
460 : : static int
461 : 0 : check_monitor(struct pmd_core_cfg *cfg, const union queue *qdata)
462 : : {
463 : : struct rte_power_monitor_cond dummy;
464 : : bool multimonitor_supported;
465 : :
466 : : /* check if rte_power_monitor is supported */
467 [ # # ]: 0 : if (!global_data.intrinsics_support.power_monitor) {
468 : 0 : POWER_LOG(DEBUG, "Monitoring intrinsics are not supported");
469 : 0 : return -ENOTSUP;
470 : : }
471 : : /* check if multi-monitor is supported */
472 : : multimonitor_supported =
473 : 0 : global_data.intrinsics_support.power_monitor_multi;
474 : :
475 : : /* if we're adding a new queue, do we support multiple queues? */
476 [ # # # # ]: 0 : if (cfg->n_queues > 0 && !multimonitor_supported) {
477 : 0 : POWER_LOG(DEBUG, "Monitoring multiple queues is not supported");
478 : 0 : return -ENOTSUP;
479 : : }
480 : :
481 : : /* check if the device supports the necessary PMD API */
482 [ # # ]: 0 : if (rte_eth_get_monitor_addr(qdata->portid, qdata->qid,
483 : : &dummy) == -ENOTSUP) {
484 : 0 : POWER_LOG(DEBUG, "The device does not support rte_eth_get_monitor_addr");
485 : 0 : return -ENOTSUP;
486 : : }
487 : :
488 : : /* we're done */
489 : : return 0;
490 : : }
491 : :
492 : : static inline rte_rx_callback_fn
493 : : get_monitor_callback(void)
494 : : {
495 : 0 : return global_data.intrinsics_support.power_monitor_multi ?
496 [ # # ]: 0 : clb_multiwait : clb_umwait;
497 : : }
498 : :
499 : : int
500 : 0 : rte_power_ethdev_pmgmt_queue_enable(unsigned int lcore_id, uint16_t port_id,
501 : : uint16_t queue_id, enum rte_power_pmd_mgmt_type mode)
502 : : {
503 : 0 : const union queue qdata = {.portid = port_id, .qid = queue_id};
504 : : struct pmd_core_cfg *lcore_cfg;
505 : : struct queue_list_entry *queue_cfg;
506 : : struct rte_eth_dev_info info;
507 : : rte_rx_callback_fn clb;
508 : : int ret;
509 : :
510 [ # # ]: 0 : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
511 : :
512 [ # # ]: 0 : if (queue_id >= RTE_MAX_QUEUES_PER_PORT || lcore_id >= RTE_MAX_LCORE) {
513 : : ret = -EINVAL;
514 : 0 : goto end;
515 : : }
516 : :
517 [ # # ]: 0 : if (rte_eth_dev_info_get(port_id, &info) < 0) {
518 : : ret = -EINVAL;
519 : 0 : goto end;
520 : : }
521 : :
522 : : /* check if queue id is valid */
523 [ # # ]: 0 : if (queue_id >= info.nb_rx_queues) {
524 : : ret = -EINVAL;
525 : 0 : goto end;
526 : : }
527 : :
528 : : /* check if the queue is stopped */
529 : 0 : ret = queue_stopped(port_id, queue_id);
530 [ # # ]: 0 : if (ret != 1) {
531 : : /* error means invalid queue, 0 means queue wasn't stopped */
532 [ # # ]: 0 : ret = ret < 0 ? -EINVAL : -EBUSY;
533 : 0 : goto end;
534 : : }
535 : :
536 : 0 : init_lcore_cfgs();
537 : 0 : lcore_cfg = RTE_LCORE_VAR_LCORE(lcore_id, lcore_cfgs);
538 : :
539 : : /* check if other queues are stopped as well */
540 : 0 : ret = cfg_queues_stopped(lcore_cfg);
541 [ # # ]: 0 : if (ret != 1) {
542 : : /* error means invalid queue, 0 means queue wasn't stopped */
543 [ # # ]: 0 : ret = ret < 0 ? -EINVAL : -EBUSY;
544 : 0 : goto end;
545 : : }
546 : :
547 : : /* if callback was already enabled, check current callback type */
548 [ # # ]: 0 : if (lcore_cfg->pwr_mgmt_state != PMD_MGMT_DISABLED &&
549 [ # # ]: 0 : lcore_cfg->cb_mode != mode) {
550 : : ret = -EINVAL;
551 : 0 : goto end;
552 : : }
553 : :
554 : : /* we need this in various places */
555 : 0 : rte_cpu_get_intrinsics_support(&global_data.intrinsics_support);
556 : :
557 [ # # # # ]: 0 : switch (mode) {
558 : 0 : case RTE_POWER_MGMT_TYPE_MONITOR:
559 : : /* check if we can add a new queue */
560 : 0 : ret = check_monitor(lcore_cfg, &qdata);
561 [ # # ]: 0 : if (ret < 0)
562 : 0 : goto end;
563 : :
564 : : clb = get_monitor_callback();
565 : : break;
566 : 0 : case RTE_POWER_MGMT_TYPE_SCALE:
567 : : clb = clb_scale_freq;
568 : :
569 : : /* we only have to check this when enabling first queue */
570 [ # # ]: 0 : if (lcore_cfg->pwr_mgmt_state != PMD_MGMT_DISABLED)
571 : : break;
572 : : /* check if we can add a new queue */
573 : 0 : ret = check_scale(lcore_id);
574 [ # # ]: 0 : if (ret < 0)
575 : 0 : goto end;
576 : : break;
577 : 0 : case RTE_POWER_MGMT_TYPE_PAUSE:
578 : : /* figure out various time-to-tsc conversions */
579 [ # # ]: 0 : if (global_data.tsc_per_us == 0)
580 : 0 : calc_tsc();
581 : :
582 : : clb = clb_pause;
583 : : break;
584 : 0 : default:
585 : 0 : POWER_LOG(DEBUG, "Invalid power management type");
586 : : ret = -EINVAL;
587 : 0 : goto end;
588 : : }
589 : : /* add this queue to the list */
590 : 0 : ret = queue_list_add(lcore_cfg, &qdata);
591 [ # # ]: 0 : if (ret < 0) {
592 : 0 : POWER_LOG(DEBUG, "Failed to add queue to list: %s",
593 : : strerror(-ret));
594 : 0 : goto end;
595 : : }
596 : : /* new queue is always added last */
597 : 0 : queue_cfg = TAILQ_LAST(&lcore_cfg->head, queue_list_head);
598 : :
599 : : /* when enabling first queue, ensure sleep target is not 0 */
600 [ # # # # ]: 0 : if (lcore_cfg->n_queues == 1 && lcore_cfg->sleep_target == 0)
601 : 0 : lcore_cfg->sleep_target = 1;
602 : :
603 : : /* initialize data before enabling the callback */
604 [ # # ]: 0 : if (lcore_cfg->n_queues == 1) {
605 : 0 : lcore_cfg->cb_mode = mode;
606 : 0 : lcore_cfg->pwr_mgmt_state = PMD_MGMT_ENABLED;
607 : : }
608 : 0 : queue_cfg->cb = rte_eth_add_rx_callback(port_id, queue_id,
609 : : clb, queue_cfg);
610 : :
611 : : ret = 0;
612 : : end:
613 : : return ret;
614 : : }
615 : :
616 : : int
617 : 0 : rte_power_ethdev_pmgmt_queue_disable(unsigned int lcore_id,
618 : : uint16_t port_id, uint16_t queue_id)
619 : : {
620 : 0 : const union queue qdata = {.portid = port_id, .qid = queue_id};
621 : : struct pmd_core_cfg *lcore_cfg;
622 : : struct queue_list_entry *queue_cfg;
623 : : int ret;
624 : :
625 [ # # ]: 0 : RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -EINVAL);
626 : :
627 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE || queue_id >= RTE_MAX_QUEUES_PER_PORT)
628 : : return -EINVAL;
629 : :
630 : : /* check if the queue is stopped */
631 : 0 : ret = queue_stopped(port_id, queue_id);
632 [ # # ]: 0 : if (ret != 1) {
633 : : /* error means invalid queue, 0 means queue wasn't stopped */
634 [ # # ]: 0 : return ret < 0 ? -EINVAL : -EBUSY;
635 : : }
636 : :
637 : : /* no need to check queue id as wrong queue id would not be enabled */
638 : :
639 : 0 : init_lcore_cfgs();
640 : 0 : lcore_cfg = RTE_LCORE_VAR_LCORE(lcore_id, lcore_cfgs);
641 : :
642 : : /* check if other queues are stopped as well */
643 : 0 : ret = cfg_queues_stopped(lcore_cfg);
644 [ # # ]: 0 : if (ret != 1) {
645 : : /* error means invalid queue, 0 means queue wasn't stopped */
646 [ # # ]: 0 : return ret < 0 ? -EINVAL : -EBUSY;
647 : : }
648 : :
649 [ # # ]: 0 : if (lcore_cfg->pwr_mgmt_state != PMD_MGMT_ENABLED)
650 : : return -EINVAL;
651 : :
652 : : /*
653 : : * There is no good/easy way to do this without race conditions, so we
654 : : * are just going to throw our hands in the air and hope that the user
655 : : * has read the documentation and has ensured that ports are stopped at
656 : : * the time we enter the API functions.
657 : : */
658 : 0 : queue_cfg = queue_list_take(lcore_cfg, &qdata);
659 [ # # ]: 0 : if (queue_cfg == NULL)
660 : : return -ENOENT;
661 : :
662 : : /* if we've removed all queues from the lists, set state to disabled */
663 [ # # ]: 0 : if (lcore_cfg->n_queues == 0)
664 : 0 : lcore_cfg->pwr_mgmt_state = PMD_MGMT_DISABLED;
665 : :
666 [ # # # ]: 0 : switch (lcore_cfg->cb_mode) {
667 : 0 : case RTE_POWER_MGMT_TYPE_MONITOR: /* fall-through */
668 : : case RTE_POWER_MGMT_TYPE_PAUSE:
669 : 0 : rte_eth_remove_rx_callback(port_id, queue_id, queue_cfg->cb);
670 : 0 : break;
671 : 0 : case RTE_POWER_MGMT_TYPE_SCALE:
672 : 0 : rte_eth_remove_rx_callback(port_id, queue_id, queue_cfg->cb);
673 : : /* disable power library on this lcore if this was last queue */
674 [ # # ]: 0 : if (lcore_cfg->pwr_mgmt_state == PMD_MGMT_DISABLED) {
675 : 0 : rte_power_freq_max(lcore_id);
676 : 0 : rte_power_exit(lcore_id);
677 : : }
678 : : break;
679 : : }
680 : : /*
681 : : * the API doc mandates that the user stops all processing on affected
682 : : * ports before calling any of these API's, so we can assume that the
683 : : * callbacks can be freed. we're intentionally casting away const-ness.
684 : : */
685 : 0 : rte_free((void *)(uintptr_t)queue_cfg->cb);
686 : 0 : free(queue_cfg);
687 : :
688 : 0 : return 0;
689 : : }
690 : :
691 : : void
692 : 0 : rte_power_pmd_mgmt_set_emptypoll_max(unsigned int max)
693 : : {
694 : 0 : emptypoll_max = max;
695 : 0 : }
696 : :
697 : : unsigned int
698 : 0 : rte_power_pmd_mgmt_get_emptypoll_max(void)
699 : : {
700 : 0 : return emptypoll_max;
701 : : }
702 : :
703 : : int
704 : 0 : rte_power_pmd_mgmt_set_pause_duration(unsigned int duration)
705 : : {
706 [ # # ]: 0 : if (duration == 0) {
707 : 0 : POWER_LOG(ERR, "Pause duration must be greater than 0, value unchanged");
708 : 0 : return -EINVAL;
709 : : }
710 : 0 : pause_duration = duration;
711 : :
712 : 0 : return 0;
713 : : }
714 : :
715 : : unsigned int
716 : 0 : rte_power_pmd_mgmt_get_pause_duration(void)
717 : : {
718 : 0 : return pause_duration;
719 : : }
720 : :
721 : : int
722 : 0 : rte_power_pmd_mgmt_set_scaling_freq_min(unsigned int lcore, unsigned int min)
723 : : {
724 [ # # ]: 0 : if (lcore >= RTE_MAX_LCORE) {
725 : 0 : POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
726 : 0 : return -EINVAL;
727 : : }
728 : :
729 [ # # ]: 0 : if (min > scale_freq_max[lcore]) {
730 : 0 : POWER_LOG(ERR, "Invalid min frequency: Cannot be greater than max frequency");
731 : 0 : return -EINVAL;
732 : : }
733 : 0 : scale_freq_min[lcore] = min;
734 : :
735 : 0 : return 0;
736 : : }
737 : :
738 : : int
739 : 0 : rte_power_pmd_mgmt_set_scaling_freq_max(unsigned int lcore, unsigned int max)
740 : : {
741 [ # # ]: 0 : if (lcore >= RTE_MAX_LCORE) {
742 : 0 : POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
743 : 0 : return -EINVAL;
744 : : }
745 : :
746 : : /* Zero means 'not set'. Use UINT32_MAX to enable RTE_MIN/MAX macro use when scaling. */
747 [ # # ]: 0 : if (max == 0)
748 : : max = UINT32_MAX;
749 [ # # ]: 0 : if (max < scale_freq_min[lcore]) {
750 : 0 : POWER_LOG(ERR, "Invalid max frequency: Cannot be less than min frequency");
751 : 0 : return -EINVAL;
752 : : }
753 : :
754 : 0 : scale_freq_max[lcore] = max;
755 : :
756 : 0 : return 0;
757 : : }
758 : :
759 : : int
760 : 0 : rte_power_pmd_mgmt_get_scaling_freq_min(unsigned int lcore)
761 : : {
762 [ # # ]: 0 : if (lcore >= RTE_MAX_LCORE) {
763 : 0 : POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
764 : 0 : return -EINVAL;
765 : : }
766 : :
767 [ # # ]: 0 : if (scale_freq_max[lcore] == 0)
768 : 0 : POWER_LOG(DEBUG, "Scaling freq min config not set. Using sysfs min freq.");
769 : :
770 : 0 : return scale_freq_min[lcore];
771 : : }
772 : :
773 : : int
774 : 0 : rte_power_pmd_mgmt_get_scaling_freq_max(unsigned int lcore)
775 : : {
776 [ # # ]: 0 : if (lcore >= RTE_MAX_LCORE) {
777 : 0 : POWER_LOG(ERR, "Invalid lcore ID: %u", lcore);
778 : 0 : return -EINVAL;
779 : : }
780 : :
781 [ # # ]: 0 : if (scale_freq_max[lcore] == UINT32_MAX) {
782 : 0 : POWER_LOG(DEBUG, "Scaling freq max config not set. Using sysfs max freq.");
783 : 0 : return 0;
784 : : }
785 : :
786 : 0 : return scale_freq_max[lcore];
787 : : }
788 : :
789 : 252 : RTE_INIT(rte_power_ethdev_pmgmt_init) {
790 : : int i;
791 : :
792 : : /* initialize config defaults */
793 : 252 : emptypoll_max = 512;
794 : 252 : pause_duration = 1;
795 : : /* scaling defaults out of range to ensure not used unless set by user or app */
796 [ + + ]: 32508 : for (i = 0; i < RTE_MAX_LCORE; i++) {
797 : 32256 : scale_freq_min[i] = 0;
798 : 32256 : scale_freq_max[i] = UINT32_MAX;
799 : : }
800 : 252 : }
|