/* htop - Scheduling.c (C) 2023 htop dev team Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "Scheduling.h" #ifdef SCHEDULER_SUPPORT #include #include #include "FunctionBar.h" #include "ListItem.h" #include "Macros.h" #include "Object.h" #include "Panel.h" #include "XUtils.h" static const SchedulingPolicy policies[] = { [SCHED_OTHER] = { "Other", SCHED_OTHER, false }, #ifdef SCHED_BATCH [SCHED_BATCH] = { "Batch", SCHED_BATCH, false }, #endif #ifdef SCHED_IDLE [SCHED_IDLE] = { "Idle", SCHED_IDLE, false }, #endif [SCHED_FIFO] = { "FiFo", SCHED_FIFO, true }, [SCHED_RR] = { "RoundRobin", SCHED_RR, true }, }; #ifdef SCHED_RESET_ON_FORK static bool reset_on_fork = false; #endif Panel* Scheduling_newPolicyPanel(int preSelectedPolicy) { Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel ")); Panel_setHeader(this, "New policy:"); #ifdef SCHED_RESET_ON_FORK Panel_add(this, (Object*) ListItem_new(reset_on_fork ? "Reset on fork: on" : "Reset on fork: off", -1)); #endif for (unsigned i = 0; i < ARRAYSIZE(policies); i++) { if (!policies[i].name) continue; Panel_add(this, (Object*) ListItem_new(policies[i].name, policies[i].id)); if (policies[i].id == preSelectedPolicy) Panel_setSelected(this, i); } return this; } void Scheduling_togglePolicyPanelResetOnFork(Panel* schedPanel) { #ifdef SCHED_RESET_ON_FORK reset_on_fork = !reset_on_fork; ListItem* item = (ListItem*) Panel_get(schedPanel, 0); free_and_xStrdup(&item->value, reset_on_fork ? "Reset on fork: on" : "Reset on fork: off"); #else (void)schedPanel; #endif } Panel* Scheduling_newPriorityPanel(int policy, int preSelectedPriority) { if (policy < 0 || (unsigned)policy >= ARRAYSIZE(policies) || policies[policy].name == NULL) return NULL; if (!policies[policy].prioritySupport) return NULL; int min = sched_get_priority_min(policy); if (min < 0) return NULL; int max = sched_get_priority_max(policy); if (max < 0 ) return NULL; Panel* this = Panel_new(0, 0, 0, 0, Class(ListItem), true, FunctionBar_newEnterEsc("Select ", "Cancel ")); Panel_setHeader(this, "Priority:"); for (int i = min; i <= max; i++) { char buf[16]; xSnprintf(buf, sizeof(buf), "%d", i); Panel_add(this, (Object*) ListItem_new(buf, i)); if (i == preSelectedPriority) Panel_setSelected(this, i); } return this; } static bool Scheduling_setPolicy(Process* p, Arg arg) { const SchedulingArg* sarg = arg.v; int policy = sarg->policy; assert(policy >= 0); assert((unsigned)policy < ARRAYSIZE(policies)); assert(policies[policy].name); const struct sched_param param = { .sched_priority = policies[policy].prioritySupport ? sarg->priority : 0 }; #ifdef SCHED_RESET_ON_FORK if (reset_on_fork) policy &= SCHED_RESET_ON_FORK; #endif int r = sched_setscheduler(Process_getPid(p), policy, ¶m); /* POSIX says on success the previous scheduling policy should be returned, * but Linux always returns 0. */ return r != -1; } bool Scheduling_rowSetPolicy(Row* row, Arg arg) { Process* p = (Process*) row; assert(Object_isA((const Object*) p, (const ObjectClass*) &Process_class)); return Scheduling_setPolicy(p, arg); } const char* Scheduling_formatPolicy(int policy) { #ifdef SCHED_RESET_ON_FORK policy = policy & ~SCHED_RESET_ON_FORK; #endif switch (policy) { case SCHED_OTHER: return "OTHER"; case SCHED_FIFO: return "FIFO"; case SCHED_RR: return "RR"; #ifdef SCHED_BATCH case SCHED_BATCH: return "BATCH"; #endif #ifdef SCHED_IDLE case SCHED_IDLE: return "IDLE"; #endif #ifdef SCHED_DEADLINE case SCHED_DEADLINE: return "EDF"; #endif default: return "???"; } } /* * Gather scheduling policy (thread-specific data) */ void Scheduling_readProcessPolicy(Process* proc) { proc->scheduling_policy = sched_getscheduler(Process_getPid(proc)); } #endif /* SCHEDULER_SUPPORT */