Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sched/uclamp: Allow to reset a task uclamp constraint value

In case the user wants to stop controlling a uclamp constraint value
for a task, use the magic value -1 in sched_util_{min,max} with the
appropriate sched_flags (SCHED_FLAG_UTIL_CLAMP_{MIN,MAX}) to indicate
the reset.

The advantage over the 'additional flag' approach (i.e. introducing
SCHED_FLAG_UTIL_CLAMP_RESET) is that no additional flag has to be
exported via uapi. This avoids the need to document how this new flag
has be used in conjunction with the existing uclamp related flags.

The following subtle issue is fixed as well. When a uclamp constraint
value is set on a !user_defined uclamp_se it is currently first reset
and then set.
Fix this by AND'ing !user_defined with !SCHED_FLAG_UTIL_CLAMP which
stands for the 'sched class change' case.
The related condition 'if (uc_se->user_defined)' moved from
__setscheduler_uclamp() into uclamp_reset().

Signed-off-by: Dietmar Eggemann <dietmar.eggemann@arm.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Yun Hsiang <hsiang023167@gmail.com>
Link: https://lkml.kernel.org/r/20201113113454.25868-1-dietmar.eggemann@arm.com

authored by

Dietmar Eggemann and committed by
Peter Zijlstra
480a6ca2 b19a888c

+53 -19
+2
include/uapi/linux/sched/types.h
··· 96 96 * on a CPU with a capacity big enough to fit the specified value. 97 97 * A task with a max utilization value smaller than 1024 is more likely 98 98 * scheduled on a CPU with no more capacity than the specified value. 99 + * 100 + * A task utilization boundary can be reset by setting the attribute to -1. 99 101 */ 100 102 struct sched_attr { 101 103 __u32 size;
+51 -19
kernel/sched/core.c
··· 1413 1413 static int uclamp_validate(struct task_struct *p, 1414 1414 const struct sched_attr *attr) 1415 1415 { 1416 - unsigned int lower_bound = p->uclamp_req[UCLAMP_MIN].value; 1417 - unsigned int upper_bound = p->uclamp_req[UCLAMP_MAX].value; 1416 + int util_min = p->uclamp_req[UCLAMP_MIN].value; 1417 + int util_max = p->uclamp_req[UCLAMP_MAX].value; 1418 1418 1419 - if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) 1420 - lower_bound = attr->sched_util_min; 1421 - if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX) 1422 - upper_bound = attr->sched_util_max; 1419 + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) { 1420 + util_min = attr->sched_util_min; 1423 1421 1424 - if (lower_bound > upper_bound) 1425 - return -EINVAL; 1426 - if (upper_bound > SCHED_CAPACITY_SCALE) 1422 + if (util_min + 1 > SCHED_CAPACITY_SCALE + 1) 1423 + return -EINVAL; 1424 + } 1425 + 1426 + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX) { 1427 + util_max = attr->sched_util_max; 1428 + 1429 + if (util_max + 1 > SCHED_CAPACITY_SCALE + 1) 1430 + return -EINVAL; 1431 + } 1432 + 1433 + if (util_min != -1 && util_max != -1 && util_min > util_max) 1427 1434 return -EINVAL; 1428 1435 1429 1436 /* ··· 1445 1438 return 0; 1446 1439 } 1447 1440 1441 + static bool uclamp_reset(const struct sched_attr *attr, 1442 + enum uclamp_id clamp_id, 1443 + struct uclamp_se *uc_se) 1444 + { 1445 + /* Reset on sched class change for a non user-defined clamp value. */ 1446 + if (likely(!(attr->sched_flags & SCHED_FLAG_UTIL_CLAMP)) && 1447 + !uc_se->user_defined) 1448 + return true; 1449 + 1450 + /* Reset on sched_util_{min,max} == -1. */ 1451 + if (clamp_id == UCLAMP_MIN && 1452 + attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN && 1453 + attr->sched_util_min == -1) { 1454 + return true; 1455 + } 1456 + 1457 + if (clamp_id == UCLAMP_MAX && 1458 + attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX && 1459 + attr->sched_util_max == -1) { 1460 + return true; 1461 + } 1462 + 1463 + return false; 1464 + } 1465 + 1448 1466 static void __setscheduler_uclamp(struct task_struct *p, 1449 1467 const struct sched_attr *attr) 1450 1468 { 1451 1469 enum uclamp_id clamp_id; 1452 1470 1453 - /* 1454 - * On scheduling class change, reset to default clamps for tasks 1455 - * without a task-specific value. 1456 - */ 1457 1471 for_each_clamp_id(clamp_id) { 1458 1472 struct uclamp_se *uc_se = &p->uclamp_req[clamp_id]; 1473 + unsigned int value; 1459 1474 1460 - /* Keep using defined clamps across class changes */ 1461 - if (uc_se->user_defined) 1475 + if (!uclamp_reset(attr, clamp_id, uc_se)) 1462 1476 continue; 1463 1477 1464 1478 /* ··· 1487 1459 * at runtime. 1488 1460 */ 1489 1461 if (unlikely(rt_task(p) && clamp_id == UCLAMP_MIN)) 1490 - __uclamp_update_util_min_rt_default(p); 1462 + value = sysctl_sched_uclamp_util_min_rt_default; 1491 1463 else 1492 - uclamp_se_set(uc_se, uclamp_none(clamp_id), false); 1464 + value = uclamp_none(clamp_id); 1465 + 1466 + uclamp_se_set(uc_se, value, false); 1493 1467 1494 1468 } 1495 1469 1496 1470 if (likely(!(attr->sched_flags & SCHED_FLAG_UTIL_CLAMP))) 1497 1471 return; 1498 1472 1499 - if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN) { 1473 + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MIN && 1474 + attr->sched_util_min != -1) { 1500 1475 uclamp_se_set(&p->uclamp_req[UCLAMP_MIN], 1501 1476 attr->sched_util_min, true); 1502 1477 } 1503 1478 1504 - if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX) { 1479 + if (attr->sched_flags & SCHED_FLAG_UTIL_CLAMP_MAX && 1480 + attr->sched_util_max != -1) { 1505 1481 uclamp_se_set(&p->uclamp_req[UCLAMP_MAX], 1506 1482 attr->sched_util_max, true); 1507 1483 }