Files
QNX/utils/a/aps/aps.c
2025-08-20 19:02:58 +08:00

1214 lines
32 KiB
C

/*
* $QNXLicenseC:
* Copyright 2007, QNX Software Systems. All Rights Reserved.
*
* You must obtain a written license from and pay applicable license fees to QNX
* Software Systems before you may reproduce, modify or distribute this software,
* or any work that includes all or part of this software. Free development
* licenses are available for evaluation and non-commercial purposes. For more
* information visit http://licensing.qnx.com or email licensing@qnx.com.
*
* This file may contain contributions from others. Please review this entire
* file for other proprietary rights or license notices, as well as the QNX
* Development Suite License Guide at http://licensing.qnx.com/license-guide/
* for other information.
* $
*/
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <libgen.h>
#include <signal.h>
#include <spawn.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <grp.h>
#include <shadow.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/neutrino.h>
#include <sys/sched_aps.h>
#include <sys/siginfo.h>
#include <sched.h>
#include <sys/syspage.h>
char * fullprogname = 0;
//============================================================
/*
IMPORTANT : Reserved for future use. Note however that there is
*NO PLAN* at present for CPU affinity, and it may never happen.
'-n' if we ever allow aps to operate on remote node like on/pidin -n.
'-C' for cpu specification (if we ever have cpu affinity for partitions)
'-R' for runmask specification (if we ever have cpu affinity for partitions)
#ifdef __USAGE
%C - Manage adaptive scheduler partitions
%C show [-d <delay>] [-f <shorthand>] [-l] [-v...] [<partition_name> ...]
%C create -b <budget> [-B <critical_budget>] <partition_name>
%C modify [-b <budget>] [-B <critical_budget>] <partition_name>
%C modify [-y <bankruptcy_policy> ...] [-s <sec_policy> ...]
[-S <sched_policy> ...] [-w <windowsize_ms>]
show:
-d delay Delay period in tenths of a second [defaults: 50]
-f shorthand Display various information based on the shorthand specified:
all
overall_stats
scheduler
partitions
usage [default]
-l Display at intervals decided by the -d option
-v More 'v's means more verbosity/details
Particularly useful for displaying 'usage'.
create:
-b budget Specify cpu budget percentage
-B milliseconds Specify critical cpu budget in ms
modify: (partition)
-b budget Specify cpu budget percentage
-B milliseconds Specify critical cpu budget in ms
modify: (scheduler)
-S policy Specify an APS scheduling policy to use
(one of: normal [default], freetime_by_ratio, bmp_safety)
-s policy Specify one or more of the security policies to add.
Security policies cannot be removed.
(one of: root0_overall, root_makes_partitions,
sys_makes_partitions, parent_modifies,
nonzero_budgets, root_makes_critical,
sys_makes_critical, root_joins
sys_joins, parent_joins, join_self_only
partitions_locked,
recommended, flexible, basic, none [default])
-w msec Set averaging windowsize in milliseconds (8 to 400).
-y policy Set bankruptcy policy
(one of: cancel_budget, log, reboot,
basic [default], recommended, none)
#endif
*/
//============================================================
void
printwarning (char *msgfmt, ...)
{
va_list ap;
va_start(ap, msgfmt);
fprintf(stdout, "Warning: ");
vfprintf(stdout, msgfmt, ap);
fprintf(stdout, "\n");
va_end(ap);
}
void
vprinterr (char *msgfmt, va_list ap)
{
fprintf(stderr, "Error: ");
vfprintf(stderr, msgfmt, ap);
fprintf(stderr, "\n");
}
void
printerr (char *msgfmt, ...)
{
va_list ap;
va_start(ap, msgfmt);
vprinterr(msgfmt, ap);
va_end(ap);
}
void
fatalerr (char *msgfmt, ...)
{
va_list ap;
va_start(ap, msgfmt);
vprinterr(msgfmt, ap);
va_end(ap);
exit(1);
}
//============================================================
int
lookup_id (char *name)
{
/* -------
* returns -1 if cant figure out an id
*/
sched_aps_lookup_parms parm;
if (name) {
APS_INIT_DATA(&parm);
parm.name = name;
if (SchedCtl(SCHED_APS_LOOKUP, &parm, sizeof(parm)) == EOK) {
return parm.id;
}
}
return -1;
}
/*
* Note: don't assume the list is sorted.
*/
int
partition_name_is_in_list (char *name,
char **name_list, int num_names)
{
int i;
for (i=0; i < num_names; i++) {
if (strcmp(name, name_list[i]) == 0) {
return 1;
}
}
return 0;
}
//============================================================
//
// General purpose keyword to/from bitmask flags functions
//
#define OPT_COMBO 0x1
#define OPT_CLOBBER 0x2
typedef struct {
_Uint32t flag_value;
char *keyword_name;
int options; /* bit 0: it's a combo
* bit 1: replace value, instead of or'ing
*/
} keyword_t;
typedef struct {
keyword_t *keywords;
int num_keywords;
} keyword_subparm_t;
void
print_keywords_from_flags (keyword_subparm_t *subparm, char *line_prefix,
_Uint32t keyword_flags)
{
int i;
keyword_t *op;
int something_printed = 0;
for (i=0; i < subparm->num_keywords; i++){
op = &subparm->keywords[i];
if (op->options & OPT_COMBO) {
if (op->flag_value == keyword_flags) {
something_printed = 1;
printf("%s%s\n",line_prefix,
subparm->keywords[i].keyword_name);
/* No need to search further once we have an EXACT
* match for a combo */
break;
}
} else {
if (op->flag_value & keyword_flags) {
something_printed = 1;
printf("%s%s\n",line_prefix,
subparm->keywords[i].keyword_name);
}
}
}
if (! something_printed) {
printf("%snone\n", line_prefix);
}
}
/* returns: 1 if valid keywords. updates *flagp with flag value if
* keyword is valid */
int
keyword_to_flag (keyword_subparm_t *subparm, char *keyword, _Uint32t *flagp)
{
int i;
for (i=0; i<subparm->num_keywords; i++) {
if (strcmp(subparm->keywords[i].keyword_name, keyword) == 0) {
if (subparm->keywords[i].options & OPT_CLOBBER) {
*flagp = subparm->keywords[i].flag_value;
} else {
*flagp |= subparm->keywords[i].flag_value;
}
return 1;
}
}
return 0;
}
//============================================================
//
// APS Security keyword to/from flags mapping
//
/* Combos must be listed first, then, followed by the other flags
* sorted in ascending order, and finally, at the end "none". */
keyword_t security_options[] = {
{SCHED_APS_SEC_RECOMMENDED, "recommended",OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_SEC_FLEXIBLE, "flexible", OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_SEC_BASIC, "basic", OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_SEC_JOIN_SELF_ONLY, "join_self_only", 0x0},
{SCHED_APS_SEC_NONZERO_BUDGETS, "nonzero_budgets", 0x0},
{SCHED_APS_SEC_PARENT_JOINS, "parent_joins", 0x0},
{SCHED_APS_SEC_PARENT_MODIFIES, "parent_modifies", 0x0},
{SCHED_APS_SEC_PARTITIONS_LOCKED, "partitions_locked", 0x0},
{SCHED_APS_SEC_ROOT0_OVERALL, "root0_overall", 0x0},
{SCHED_APS_SEC_ROOT_JOINS, "root_joins", 0x0},
{SCHED_APS_SEC_ROOT_MAKES_CRITICAL, "root_makes_critical", 0x0},
{SCHED_APS_SEC_ROOT_MAKES_PARTITIONS, "root_makes_partitions",0x0},
{SCHED_APS_SEC_SYS_JOINS, "sys_joins", 0x0},
{SCHED_APS_SEC_SYS_MAKES_CRITICAL, "sys_makes_critical", 0x0},
{SCHED_APS_SEC_SYS_MAKES_PARTITIONS, "sys_makes_partitions", 0x0},
{SCHED_APS_SEC_OFF, "none", 0x2},
};
keyword_subparm_t security_handle_options = {
security_options,
sizeof(security_options)/sizeof(keyword_t), /* Number of keywords */
};
void
print_security_flags (char *line_prefix,
_Uint32t security_flags)
{
print_keywords_from_flags(&security_handle_options, line_prefix,
security_flags);
}
//============================================================
//
// APS bankruptcy keyword to/from flags mapping
//
/* Combos must be listed first, then, followed by the other flags
* sorted in ascending order, and finally, at the end "none". */
keyword_t bankruptcy_options[] = {
{SCHED_APS_BNKR_BASIC, "basic", OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_BNKR_RECOMMENDED, "recommended", OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_BNKR_CANCEL_BUDGET, "cancel_budget", 0x0},
{SCHED_APS_BNKR_LOG, "log", 0x0},
{SCHED_APS_BNKR_REBOOT, "reboot", 0x0},
/* 'none' is treated seperately, since a null pointer means 'none'... */
};
keyword_subparm_t bankruptcy_handle_options = {
bankruptcy_options,
sizeof(bankruptcy_options)/sizeof(keyword_t), /* Number of keywords */
};
void
print_bankruptcy_flags (char *line_prefix,
_Uint32t bankruptcy_flags)
{
print_keywords_from_flags(&bankruptcy_handle_options, line_prefix,
bankruptcy_flags);
}
//============================================================
//
// APS utility shorthand keyword to/from flags mapping
//
#define SHORTHAND_OVERALL_STATS 0x000000001
#define SHORTHAND_PARTITIONS 0x000000002
#define SHORTHAND_SCHEDULER 0x000000004
#define SHORTHAND_USAGE 0x000000008
#define SHORTHAND_ALL (SHORTHAND_OVERALL_STATS| \
SHORTHAND_PARTITIONS| \
SHORTHAND_SCHEDULER| \
SHORTHAND_USAGE)
/* Combos must be listed first, then, followed by the other flags
* sorted in ascending order, and finally, at the end "none". */
keyword_t shorthand_options[] = {
{SHORTHAND_ALL, "all", OPT_COMBO|OPT_CLOBBER},
{SHORTHAND_OVERALL_STATS, "overall_stats", 0},
{SHORTHAND_PARTITIONS, "partitions", 0},
{SHORTHAND_SCHEDULER, "scheduler", 0},
{SHORTHAND_USAGE, "usage", 0},
};
keyword_subparm_t shorthand_handle_options = {
shorthand_options,
sizeof(shorthand_options)/sizeof(keyword_t), /* Number of keywords */
};
//============================================================
//
// APS scheduling policy keyword to/from flags mapping
//
/* Combos must be listed first, then, followed by the other flags
* sorted in ascending order, and finally, at the end "none". */
keyword_t schedpol_options[] = {
{SCHED_APS_SCHEDPOL_DEFAULT, "normal", OPT_COMBO|OPT_CLOBBER},
{SCHED_APS_SCHEDPOL_FREETIME_BY_RATIO, "freetime_by_ratio", 0},
{SCHED_APS_SCHEDPOL_BMP_SAFETY, "bmp_safety", 0},
/* 'none' is treated separately, since a null pointer means 'none'... */
};
keyword_subparm_t schedpol_handle_options = {
schedpol_options,
sizeof(schedpol_options)/sizeof(keyword_t), /* Number of keywords */
};
void
print_schedpol_flags (char *line_prefix,
_Uint32t schedpol_flags)
{
print_keywords_from_flags(&schedpol_handle_options, line_prefix,
schedpol_flags);
}
//============================================================
/*
* NOTE: Do not cache the information that is printed in case the
* 'show' is looping.
*/
int
print_scheduler (void)
{
int ret;
sched_aps_info sched_info;
APS_INIT_DATA(&sched_info);
ret = SchedCtl(SCHED_APS_QUERY_PARMS, &sched_info, sizeof(sched_info));
if (ret != EOK) {
printerr("can't query scheduler parameters: %s (%d)",
strerror(errno), errno);
return ret;
}
print_security_flags("security option : ",
sched_info.sec_flags);
print_bankruptcy_flags("bankruptcy policy : ",
sched_info.bankruptcy_policy);
print_schedpol_flags("scheduling policy : ",
sched_info.scheduling_policy_flags);
{
unsigned long ws =
(sched_info.windowsize_cycles / sched_info.cycles_per_ms);
printf("window size (ms) : %lu\n", ws);
}
return ret;
}
/*
* NOTE: Do not cache the information that is printed in case the
* 'show' is looping.
*/
int
print_overall_stats (void)
{
int ret;
sched_aps_overall_stats p;
APS_INIT_DATA(&p);
ret = SchedCtl(SCHED_APS_OVERALL_STATS, &p, sizeof(p));
if (ret == EOK) {
printf("last bankrupting id : %d\n",
p.id_at_last_bankruptcy);
printf("pid,tid at last bankruptcy : %d,%d\n",
p.pid_at_last_bankruptcy, p.tid_at_last_bankruptcy);
}
return ret;
}
/* the probable maximum number of partitions we'll have to deal with */
#define APS_MAX_PARTITIONS 16
/* @@@ APS_MAX_PARTITIONS should eventually be replaced with code to read max_partitions from SCHED_APS_QUERY_PARMS */
typedef struct {
sched_aps_info sched_info;
sched_aps_partition_stats part_stats[APS_MAX_PARTITIONS];
sched_aps_partition_info part_info[APS_MAX_PARTITIONS];
} sched_and_parts_data_t;
/*
* Read all the scheduler and partitions info by keeping the system
* calls as close to one another as possible. This ensures that the
* numbers are more accurate/consistent, since it's not impossible
* that the numbers change as we read them.
*
* Return: a pointer to the data that the caller is responsible to
* free, or return 0 (NULL) if the memory could not be allocated.
*
* Note: the caller should not access member part_stats, unless the
* function was invoked with include_part_stats=1.
*/
sched_and_parts_data_t *
read_all_sched_and_parts (int include_part_stats)
{
sched_and_parts_data_t *d;
int i;
int ret = EOK;
d = (sched_and_parts_data_t*)calloc(1, sizeof(sched_and_parts_data_t));
if (d == 0) {
printerr("unable to allocate memory");
return 0;
}
APS_INIT_DATA(&(d->sched_info));
ret = SchedCtl(SCHED_APS_QUERY_PARMS,
&(d->sched_info), sizeof(d->sched_info));
if (ret != EOK) {
printerr("can't query scheduler parms: %s (%d)",
strerror(errno), errno);
} else {
if (include_part_stats) {
APS_INIT_DATA(&(d->part_stats));
d->part_stats[0].id = 0;
ret = SchedCtl(SCHED_APS_PARTITION_STATS,
&(d->part_stats), sizeof(d->part_stats));
} else {
ret = EOK;
}
if (ret != EOK) {
printerr("can't read partition stats: %s (%d)",
strerror(errno), errno);
} else {
for(i = 0; i < d->sched_info.num_partitions; i++) {
APS_INIT_DATA(&(d->part_info[i]));
d->part_info[i].id = i;
ret = SchedCtl(SCHED_APS_QUERY_PARTITION,
&(d->part_info[i]),
sizeof(d->part_info[0]));
if (ret != EOK) {
printerr("can't get info for partition %d: %s (%d)",
i, strerror(errno), errno);
break;
}
}
}
}
if (ret != EOK) {
free(d);
return 0;
}
return d;
}
/*
* NOTE: Do not cache the information that is printed in case the
* 'show' is looping.
*/
int
print_partitions (char **partition_names, int num_partition_names)
{
int i;
sched_and_parts_data_t * d;
/* Note: data must be freed at the end */
d = read_all_sched_and_parts(0 /* do not include part_stats */);
if (d == 0) {
return -1;
}
printf("%-*s id parentId %% ms pidBnkr tidBnkr\n",
APS_PARTITION_NAME_LENGTH,
"PartitionName");
for(i = 0; i < d->sched_info.num_partitions; i++) {
if (num_partition_names == 0 ||
partition_name_is_in_list(d->part_info[i].name,
partition_names,
num_partition_names)) {
printf("%-*s %3d %10d %3d %3d %10d %10d\n",
APS_PARTITION_NAME_LENGTH,
d->part_info[i].name,
i,
d->part_info[i].parent_id,
d->part_info[i].budget_percent,
(int)(d->part_info[i].critical_budget_cycles /
d->sched_info.cycles_per_ms),
d->part_info[i].pid_at_last_bankruptcy,
d->part_info[i].tid_at_last_bankruptcy);
}
}
free(d);
return EOK;
}
typedef enum {
pg_nothing = 0,
pg_simple,
pg_enhancedused,
pg_enhancedused_enhancedcritical,
pg_max_level = pg_enhancedused_enhancedcritical,
} pg_detail_level_t;
/*
* NOTE: Do not cache the information that is printed in case the
* 'show' is looping.
*/
int
print_groups_usage (int detail_level,
char **partition_names, int num_names)
{
int i;
int total_per_bud;
double total_per_used, total_per_w2, total_per_w3;
double sched_windowsize_s, windowsize2_s, windowsize3_s;
pg_detail_level_t pg_detail_level;
sched_and_parts_data_t *d;
static char * long_dash_string="----------------------------------------";
/* Note: data must be freed at the end */
d = read_all_sched_and_parts(1 /* include part_stats */);
if (d == 0) {
return -1;
}
if (detail_level < pg_simple) {
pg_detail_level = pg_simple;
} else if (detail_level > pg_enhancedused_enhancedcritical) {
pg_detail_level = pg_enhancedused_enhancedcritical;
} else {
pg_detail_level = detail_level;
}
sched_windowsize_s =
(double)d->sched_info.windowsize_cycles /
(double)d->sched_info.cycles_per_ms/1000.0;
windowsize2_s =
(double)d->sched_info.windowsize2_cycles /
(double)d->sched_info.cycles_per_ms/1000.0;
windowsize3_s =
(double)d->sched_info.windowsize3_cycles /
(double)d->sched_info.cycles_per_ms/1000.0;
switch(pg_detail_level) {
case pg_nothing:
break;
case pg_simple:
printf("%*.*s +---- CPU Time ----+-- Critical Time --\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "");
printf("%-*.*s id | Budget | Used | Budget | Used\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "Partition name");
printf("%*.*s-----+------------------+-------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
break;
case pg_enhancedused:
printf("%*.*s +----------- CPU Time ------------+-- Critical Time --\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "");
printf(" | | Used | |\n");
printf("%-*.*s id | Budget | %2.3fs %2.2fs %2.1fs | Budget | Used\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "Partition name",
sched_windowsize_s, windowsize2_s, windowsize3_s);
printf("%*.*s-----+---------------------------------+-------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
break;
case pg_enhancedused_enhancedcritical:
printf("%*.*s +----------- CPU Time ------------+------------ Critical Time ------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "");
printf("%*.*s | | Used | | Used\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "");
printf("Partition name id | Budget | %2.3fs %2.2fs %2.1fs | Budget | %2.1fs %2.1fs %2.1fs\n",
sched_windowsize_s, windowsize2_s, windowsize3_s,
sched_windowsize_s, windowsize2_s, windowsize3_s);
printf("%*.*s-----+---------------------------------+---------------------------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
break;
}
total_per_used = total_per_w2 = total_per_w3 = 0.0;
total_per_bud = 0;
for(i = 0; i < d->sched_info.num_partitions; i++) {
double per, per_w2,per_w3;
int critical_budget_ms;
double critical_used_ms, critical_used_ms_w2, critical_used_ms_w3;
if (num_names == 0 ||
partition_name_is_in_list(d->part_info[i].name,
partition_names, num_names)) {
critical_used_ms =
(double)d->part_stats[i].critical_time_cycles /
(double)d->sched_info.cycles_per_ms;
critical_used_ms_w2 =
(double)d->part_stats[i].critical_time_cycles_w2 /
(double)d->sched_info.cycles_per_ms;
critical_used_ms_w3 =
(double)d->part_stats[i].critical_time_cycles_w3 /
(double)d->sched_info.cycles_per_ms;
critical_budget_ms =
d->part_info[i].critical_budget_cycles /
d->sched_info.cycles_per_ms;
total_per_bud += d->part_info[i].budget_percent;
per =
(((double)100.0)*(double)d->part_stats[i].run_time_cycles) /
(double)d->sched_info.windowsize_cycles;
total_per_used +=per;
per_w2 =
(((double)100.0)*(double)d->part_stats[i].run_time_cycles_w2) /
(double)d->sched_info.windowsize2_cycles;
total_per_w2 += per_w2;
per_w3 =
(((double)100.0)*(double)d->part_stats[i].run_time_cycles_w3) /
(double)d->sched_info.windowsize3_cycles;
total_per_w3 += per_w3;
switch(pg_detail_level) {
case pg_nothing:
break;
case pg_simple:
printf("%-*.*s %3d | %3d%% | %6.2f%% | %3dms | %7.3fms\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, d->part_info[i].name,
i,
d->part_info[i].budget_percent,
per, critical_budget_ms, critical_used_ms);
break;
case pg_enhancedused:
printf("%-*.*s %3d | %3d%% |%6.2f%% %6.2f%% %6.2f%% | %3dms | %7.3fms\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, d->part_info[i].name,
i,
d->part_info[i].budget_percent,
per, per_w2, per_w3,
critical_budget_ms, critical_used_ms);
break;
case pg_enhancedused_enhancedcritical:
printf("%-*.*s %3d | %3d%% |%6.2f%% %6.2f%% %6.2f%% | %3dms | %7.3fms %7.3fms %7.3fms\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, d->part_info[i].name,
i,
d->part_info[i].budget_percent,
per, per_w2, per_w3,
critical_budget_ms, critical_used_ms,
critical_used_ms_w2, critical_used_ms_w3);
break;
}
}
}
/* Only print totals if all partitions are displayed */
switch(pg_detail_level) {
case pg_nothing:
break;
case pg_simple:
printf("%*.*s-----+------------------+-------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
printf("%-*.*s | %3d%% | %6.2f%% |\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "Total",
total_per_bud, total_per_used);
break;
case pg_enhancedused:
printf("%*.*s-----+---------------------------------+-------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
printf("%-*.*s | %3d%% |%6.2f%% %6.2f%% %6.2f%% |\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "Total",
total_per_bud, total_per_used,
total_per_w2, total_per_w3);
break;
case pg_enhancedused_enhancedcritical:
printf("%*.*s-----+---------------------------------+---------------------------------------\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, long_dash_string);
printf("%-*.*s | %3d%% |%6.2f%% %6.2f%% %6.2f%% |\n",
APS_PARTITION_NAME_LENGTH, APS_PARTITION_NAME_LENGTH, "Total",
total_per_bud, total_per_used,
total_per_w2, total_per_w3);
break;
}
free(d);
return EOK;
}
//============================================================
int
show_cmd (int argc, char **argv)
{
int has_error = 0;
int o;
unsigned long number;
char **partition_names = 0;
int num_partition_names = 0;
int detail_level = 1;
int do_loop = 0;
long delay = 50;
char *rest;
char *shorthand = 0;
_Uint32t shorthand_flags = 0;
optind = 1;
while((o = getopt(argc, argv, ":d:vlf:")) != -1) {
switch(o) {
case 'd':
rest = 0;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for '-%c'", optarg, o);
}
delay = number;
break;
case 'v': /* partition usage information */
detail_level++;
break;
case 'l':
do_loop = 1;
break;
case 'f':
shorthand = optarg;
if (keyword_to_flag(&shorthand_handle_options,
optarg, &shorthand_flags) == 0) {
fatalerr("invalid shorthand \"%s\"", optarg);
}
break;
case ':':
fatalerr("missing argument for '-%c'", optopt);
break;
case '?':
fatalerr("unknown option '-%c'", optopt);
break;
}
}
if (optind < argc) {
partition_names = &(argv[optind]);
num_partition_names = argc-optind;
}
if (!shorthand_flags) {
shorthand_flags = SHORTHAND_USAGE;
}
while (!has_error) {
int add_line = 0;
if (shorthand_flags & SHORTHAND_SCHEDULER) {
if (print_scheduler() != EOK) {
has_error = 1;
}
add_line = 1;
}
if (shorthand_flags & SHORTHAND_OVERALL_STATS) {
if (add_line) printf("\n");
if (print_overall_stats() != EOK) {
has_error = 1;
}
add_line = 1;
}
if (shorthand_flags & SHORTHAND_PARTITIONS) {
if (add_line) printf("\n");
if (print_partitions(partition_names,
num_partition_names) != EOK) {
has_error = 1;
}
add_line = 1;
}
if (shorthand_flags & SHORTHAND_USAGE) {
if (add_line) printf("\n");
if (print_groups_usage(detail_level,
partition_names,
num_partition_names) != EOK) {
has_error = 1;
}
add_line = 1;
}
if (!has_error) {
if (do_loop == 0 || delay == 0) {
break;
} else {
useconds_t us = delay*100000;
if (add_line) printf("\n");
if (usleep(us)) {
has_error = 1;
} else {
printf("\n");
}
}
}
}
if (has_error) {
return 1;
}
return 0;
}
int
create_cmd (int argc, char **argv)
{
int has_error = 0;
int o;
char *rest;
sched_aps_create_parms p;
int ret;
unsigned long number;
int opt_b_cnt = 0;
APS_INIT_DATA(&p);
p.budget_percent = 0;
p.critical_budget_ms = 0;
p.name = 0;
optind = 1;
while ((o = getopt(argc, argv, ":b:B:")) != -1) {
switch (o) {
case 'b': /* budget */
opt_b_cnt ++;
rest = 0;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for -%c", optarg, o);
}
p.budget_percent = number;
break;
case 'B': /* Critical budget (ms) */
rest = 0;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for '-%c'", optarg, o);
}
p.critical_budget_ms = number;
break;
case ':':
fatalerr("missing argument for '-%c'", optopt);
break;
case '?':
fatalerr("unknown option '-%c'", optopt);
break;
}
}
if (opt_b_cnt == 0) {
fatalerr("must specify -b <budget_percent>");
}
if (optind != argc-1) {
fatalerr("must specify a single partition name as operand");
}
p.name = argv[optind];
ret = SchedCtl(SCHED_APS_CREATE_PARTITION, &p, sizeof(p));
if (ret != EOK) {
printerr("couldn't create partition \"%s\": %s (%d)",
p.name, strerror(errno), errno);
has_error = 1;
}
if (has_error) {
return 1;
} else {
return 0;
}
}
/*
* Modify either the specified partition or the scheduller.
*
* Note: There's been much talk about whether to use a single 'modify'
* command, or one for partition modifications and one for scheduler
* modifications, and no clear advantage of one approach vs the
* other... so...
*/
int modify_cmd (int argc, char **argv)
{
int has_error = 0;
char *rest;
int o;
unsigned long number;
/* Track whether there are changes to make to the partition, or
* the scheduler, given that both a partion and a scheduler can be
* changed */
int do_change_scheduler = 0;
int do_change_partition = 0;
char *partition_name = 0;
sched_aps_modify_parms b; /* budget related, hence b */
sched_aps_parms p; /* bankruptcy, windowsize */
_Uint32t bankruptcy_policy;
_Uint32t scheduling_policy;
sched_aps_security_parms s; /* security */
int do_change_security_policy = 0;
APS_INIT_DATA(&b);
b.id = -1;
b.new_budget_percent = -1;
b.new_critical_budget_ms = -1;
APS_INIT_DATA(&p);
p.bankruptcy_policyp = NULL;
p.scheduling_policy_flagsp = NULL;
bankruptcy_policy = 0;
scheduling_policy = 0;
p.windowsize_ms = -1;
APS_INIT_DATA(&s);
s.sec_flags = 0;
optind=1;
while ((o = getopt(argc, argv, ":b:B:s:S:w:y:")) != -1) {
switch (o) {
case 'b': /* budget (percent) */
{
rest = 0;
do_change_partition = 1;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for '-%c'", optarg, o);
}
b.new_budget_percent = number;
}
break;
case 'B': /* Critical budget (ms) */
{
rest = 0;
do_change_partition = 1;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for '-%c'", optarg, o);
}
b.new_critical_budget_ms = number;
}
break;
case 's': /* security policy */
{
do_change_scheduler = 1;
do_change_security_policy = 1;
/* Security is cumulative, so preserve current flag,
* and add to it */
if (keyword_to_flag(&security_handle_options,
optarg,
&(s.sec_flags)) == 0) {
fatalerr("invalid security policy \"%s\"", optarg);
}
}
break;
case 'S': /* scheduling policy */
{
do_change_scheduler = 1;
/*
* Scheduling policy isn't cumulative, but
* may be so one day. Preserver current flag,
* and add to it
*/
if (keyword_to_flag(&schedpol_handle_options,
optarg,
&scheduling_policy) == 0) {
fatalerr("invalid scheduling policy \"%s\"", optarg);
}
p.scheduling_policy_flagsp = &scheduling_policy;
}
break;
case 'w': /* window size */
{
rest = 0;
do_change_scheduler = 1;
number = strtoul(optarg, &rest, 10);
if ((rest && rest[0] != 0) || number == ULONG_MAX) {
fatalerr("invalid value %s for '-%c'", optarg, o);
}
p.windowsize_ms = number;
}
break;
case 'y': /* Bankruptcy policy */
{
do_change_scheduler = 1;
/* Bankruptcy is cummulative, so preserve current flag
* and add to it... */
if (keyword_to_flag(&bankruptcy_handle_options,
optarg,
&bankruptcy_policy) == 0) {
fatalerr("invalid bankruptcy policy \"%s\"", optarg);
}
p.bankruptcy_policyp = &bankruptcy_policy;
}
break;
case ':':
fatalerr("missing argument for '-%c'", optopt);
break;
case '?':
fatalerr("unknown option '-%c'", optopt);
break;
}
}
if (do_change_partition) {
if (optind != argc-1) {
fatalerr("there should be one and only one partition name");
}
partition_name = argv[optind];
}
if (do_change_scheduler) {
if (optind < argc) {
/* There shouldn't be any operand */
fatalerr("operands not allowed when changing only the scheduler");
}
}
if (!do_change_partition && !do_change_scheduler) {
fatalerr("must specify a change");
}
/* Scheduler related changes ... */
if (do_change_scheduler) {
if (do_change_security_policy) {
sched_aps_security_parms s_saved = s;
int ret = SchedCtl(SCHED_APS_ADD_SECURITY, &s, sizeof(s));
if (ret != EOK) {
printerr("couldn't change security policy: %s (%d)",
strerror(errno), errno);
has_error = 1;
} else {
if (((~s_saved.sec_flags)&s.sec_flags) != 0) {
printwarning("More security policies in effect than what was specified");
}
}
}
if (p.bankruptcy_policyp || p.windowsize_ms >= 0 || p.scheduling_policy_flagsp) {
int ret = SchedCtl(SCHED_APS_SET_PARMS, &p, sizeof(p));
if (ret != EOK) {
printerr("couldn't change global APS parameters: %s (%d)",
strerror(errno), errno);
has_error = 1;
}
}
}
/* Partition related changes ... */
if (do_change_partition) {
b.id = lookup_id(partition_name);
if (b.id == -1) {
printerr("can't find partition \"%s\": %s (%d)",
partition_name, strerror(errno), errno);
has_error = 1;
} else {
if (b.new_budget_percent >= 0 || b.new_critical_budget_ms >= 0) {
int ret = SchedCtl(SCHED_APS_MODIFY_PARTITION, &b, sizeof(b));
if (ret != EOK) {
printerr("couldn't set budget: %s (%d)",
strerror(errno), errno);
has_error = 1;
}
}
}
}
if (has_error) {
return 1;
}
return 0;
}
//============================================================
int
aps_sched_is_running (void)
{
struct sched_query parms;
memset(&parms,0,sizeof(parms));
if (SchedCtl(SCHED_QUERY_SCHED_EXT,
&parms, sizeof(parms)) == EOK) {
if (parms.extsched == SCHED_EXT_APS) {
return 1;
}
}
return 0;
}
int
main (int argc, char *argv[])
{
int argindex = 1;
fullprogname = argv[0];
if (! aps_sched_is_running()) {
fatalerr("APS scheduler not running");
}
if (argc == 1) {
char *default_argv[] = {"show"};
return show_cmd(1, default_argv);
} else {
char * cmd = argv[argindex];
if (strcmp(cmd, "show") == 0) {
return show_cmd(argc-argindex, &(argv[argindex]));
} else if (strcmp(cmd, "create") == 0) {
return create_cmd(argc-argindex, &(argv[argindex]));
} else if (strcmp(cmd, "modify") == 0) {
return modify_cmd(argc-argindex, &(argv[argindex]));
} else {
fatalerr("unknown command \"%s\"", cmd);
}
}
return(1);
}
__SRCVERSION("aps.c $Rev: 153052 $");