diff options
Diffstat (limited to 'usr/src/lib/libc/port/rt/sched.c')
-rw-r--r-- | usr/src/lib/libc/port/rt/sched.c | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/usr/src/lib/libc/port/rt/sched.c b/usr/src/lib/libc/port/rt/sched.c new file mode 100644 index 0000000000..58b793f2e2 --- /dev/null +++ b/usr/src/lib/libc/port/rt/sched.c @@ -0,0 +1,552 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "synonyms.h" +#include "mtlib.h" +#include <sys/types.h> +#include <sched.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <sys/priocntl.h> +#include <sys/rtpriocntl.h> +#include <sys/tspriocntl.h> +#include <sys/rt.h> +#include <sys/ts.h> +#include <thread.h> +#include <string.h> +#include <stdlib.h> +#include "rtsched.h" + +/* + * The following variables are used for caching information + * for priocntl scheduling classes. + */ +struct pcclass ts_class; +struct pcclass rt_class; +struct pcclass ia_class; +struct pcclass sys_class; + +static rtdpent_t *rt_dptbl; /* RT class parameter table */ + +typedef struct { /* type definition for generic class-specific parameters */ + int pc_clparms[PC_CLINFOSZ]; +} pc_clparms_t; + +static int map_gp_to_rtpri(pri_t); + +/* + * cache priocntl information on scheduling classes by policy + */ +int +get_info_by_policy(int policy) +{ + char *pccname; + struct pcclass *pccp; + + if (policy < 0) { + errno = EINVAL; + return (-1); + } + + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + pccp = &rt_class; + pccname = "RT"; + break; + case SCHED_OTHER: + pccp = &ts_class; + pccname = "TS"; + break; + case SCHED_SYS: + pccp = &sys_class; + pccname = "sys"; + break; + case SCHED_IA: + pccp = &ia_class; + pccname = "IA"; + break; + default: + return (policy); + } + if (pccp->pcc_state != 0) { + if (pccp->pcc_state < 0) + errno = ENOSYS; + return (pccp->pcc_state); + } + + /* get class's info */ + (void) strcpy(pccp->pcc_info.pc_clname, pccname); + if (policy == SCHED_SYS) + pccp->pcc_info.pc_cid = 0; + else if (priocntl(P_PID, 0, PC_GETCID, (caddr_t)&(pccp->pcc_info)) < 0) + return (-1); + + if (policy == SCHED_FIFO || policy == SCHED_RR) { + pcadmin_t pcadmin; + rtadmin_t rtadmin; + size_t rtdpsize; + + /* get RT class dispatch table in rt_dptbl */ + pcadmin.pc_cid = rt_class.pcc_info.pc_cid; + pcadmin.pc_cladmin = (caddr_t)&rtadmin; + rtadmin.rt_cmd = RT_GETDPSIZE; + if (priocntl(P_PID, 0, PC_ADMIN, (caddr_t)&pcadmin) < 0) + return (-1); + rtdpsize = (size_t)(rtadmin.rt_ndpents * sizeof (rtdpent_t)); + if (rt_dptbl == NULL && + (rt_dptbl = lmalloc(rtdpsize)) == NULL) { + errno = EAGAIN; + return (-1); + } + rtadmin.rt_dpents = rt_dptbl; + rtadmin.rt_cmd = RT_GETDPTBL; + if (priocntl(P_PID, 0, PC_ADMIN, (caddr_t)&pcadmin) < 0) + return (-1); + pccp->pcc_primin = 0; + pccp->pcc_primax = ((rtinfo_t *)rt_class.pcc_info.pc_clinfo)-> + rt_maxpri; + } else if (policy == SCHED_OTHER) { + pri_t prio; + + prio = ((tsinfo_t *)ts_class.pcc_info.pc_clinfo)->ts_maxupri/3; + pccp->pcc_primin = -prio; + pccp->pcc_primax = prio; + } else { + /* non-RT scheduling class */ + pcpri_t pcpri; + + /* need RT class info before we can translate priorities */ + if (get_info_by_policy(SCHED_FIFO) < 0) + return (-1); + /* + * get class's global priority's min, max, and + * translate them into RT priority level (index) via rt_dptbl. + */ + pcpri.pc_cid = pccp->pcc_info.pc_cid; + if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) < 0) + return (-1); + pccp->pcc_primax = map_gp_to_rtpri(pcpri.pc_clpmax); + pccp->pcc_primin = map_gp_to_rtpri(pcpri.pc_clpmin); + } + + pccp->pcc_state = 1; + return (1); +} + +/* + * Translate global scheduling priority to RT class's user priority. + * Use the gp values in the rt_dptbl to do a reverse mapping + * of a given gpri value relative to the index range of rt_dptbl. + */ +static int +map_gp_to_rtpri(pri_t gpri) +{ + rtdpent_t *rtdp; + pri_t pri; + + if (gpri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { + pri = gpri - rt_dptbl[rt_class.pcc_primin].rt_globpri + \ + rt_class.pcc_primin; + } else if (gpri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { + pri = gpri - rt_dptbl[rt_class.pcc_primax].rt_globpri + \ + rt_class.pcc_primax; + } else { + pri = rt_class.pcc_primin + 1; + for (rtdp = rt_dptbl+1; rtdp->rt_globpri < gpri; ++rtdp, ++pri) + ; + if (rtdp->rt_globpri > gpri) + --pri; + } + + return (pri); +} + +/* + * Translate RT class's user priority to global scheduling priority. + */ +pri_t +map_rtpri_to_gp(pri_t pri) +{ + rtdpent_t *rtdp; + pri_t gpri; + + if (rt_class.pcc_state == 0) + (void) get_info_by_policy(SCHED_FIFO); + + /* First case is the default case, other two are seldomly taken */ + if (pri <= rt_dptbl[rt_class.pcc_primin].rt_globpri) { + gpri = pri + rt_dptbl[rt_class.pcc_primin].rt_globpri - + rt_class.pcc_primin; + } else if (pri >= rt_dptbl[rt_class.pcc_primax].rt_globpri) { + gpri = pri + rt_dptbl[rt_class.pcc_primax].rt_globpri - + rt_class.pcc_primax; + } else { + gpri = rt_dptbl[rt_class.pcc_primin].rt_globpri + 1; + for (rtdp = rt_dptbl+1; rtdp->rt_globpri < pri; ++rtdp, ++gpri) + ; + if (rtdp->rt_globpri > pri) + --gpri; + } + return (gpri); +} + +static int +get_info_by_class(id_t classid) +{ + pcinfo_t pcinfo; + + /* determine if we already know this classid */ + if (rt_class.pcc_state > 0 && rt_class.pcc_info.pc_cid == classid) + return (1); + if (ts_class.pcc_state > 0 && ts_class.pcc_info.pc_cid == classid) + return (1); + if (sys_class.pcc_state > 0 && sys_class.pcc_info.pc_cid == classid) + return (1); + if (ia_class.pcc_state > 0 && ia_class.pcc_info.pc_cid == classid) + return (1); + + pcinfo.pc_cid = classid; + if (priocntl(0, 0, PC_GETCLINFO, (caddr_t)&pcinfo) < 0) { + if (classid == 0) /* no kernel info for sys class */ + return (get_info_by_policy(SCHED_SYS)); + return (-1); + } + + if (rt_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "RT") == 0) + return (get_info_by_policy(SCHED_FIFO)); + if (ts_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "TS") == 0) + return (get_info_by_policy(SCHED_OTHER)); + if (ia_class.pcc_state == 0 && strcmp(pcinfo.pc_clname, "IA") == 0) + return (get_info_by_policy(SCHED_IA)); + + return (1); +} + +int +sched_setparam(pid_t pid, const struct sched_param *param) +{ + pri_t prio = param->sched_priority; + pcparms_t pcparm; + tsparms_t *tsp; + tsinfo_t *tsi; + int scale; + + if (pid < 0) { + errno = ESRCH; + return (-1); + } + if (pid == 0) + pid = P_MYID; + + /* get process's current scheduling policy */ + pcparm.pc_cid = PC_CLNULL; + if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) + return (-1); + if (get_info_by_class(pcparm.pc_cid) < 0) + return (-1); + + if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { + /* SCHED_FIFO or SCHED_RR policy */ + if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { + errno = EINVAL; + return (-1); + } + ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = RT_NOCHANGE; + ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; + } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { + /* SCHED_OTHER policy */ + tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; + scale = tsi->ts_maxupri; + tsp = (tsparms_t *)pcparm.pc_clparms; + tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; + } else { + /* + * policy is not defined by POSIX.4. + * just pass parameter data through to priocntl. + * param should contain an image of class-specific parameters + * (after the sched_priority member). + */ + *((pc_clparms_t *)pcparm.pc_clparms) = + *((pc_clparms_t *)(&(param->sched_priority)+1)); + } + + return ((int)priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm)); +} + +int +sched_getparam(pid_t pid, struct sched_param *param) +{ + pcparms_t pcparm; + pri_t prio; + int scale; + tsinfo_t *tsi; + + if (pid < 0) { + errno = ESRCH; + return (-1); + } + if (pid == 0) + pid = P_MYID; + + pcparm.pc_cid = PC_CLNULL; + if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) + return (-1); + if (get_info_by_class(pcparm.pc_cid) < 0) + return (-1); + + if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) { + param->sched_priority = + ((rtparms_t *)pcparm.pc_clparms)->rt_pri; + } else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) { + param->sched_nicelim = + ((tsparms_t *)pcparm.pc_clparms)->ts_uprilim; + prio = param->sched_nice = + ((tsparms_t *)pcparm.pc_clparms)->ts_upri; + tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; + scale = tsi->ts_maxupri; + if (scale == 0) + param->sched_priority = 0; + else + param->sched_priority = -(prio * 20) / scale; + } else { + /* + * policy is not defined by POSIX.4 + * just return a copy of pcparams_t image in param. + */ + *((pc_clparms_t *)(&(param->sched_priority)+1)) = + *((pc_clparms_t *)pcparm.pc_clparms); + param->sched_priority = + sched_get_priority_min((int)(pcparm.pc_cid + _SCHED_NEXT)); + } + + return (0); +} + +int +sched_setscheduler(pid_t pid, int policy, const struct sched_param *param) +{ + pri_t prio = param->sched_priority; + pcparms_t pcparm; + int oldpolicy; + tsinfo_t *tsi; + tsparms_t *tsp; + int scale; + + if ((oldpolicy = sched_getscheduler(pid)) < 0) + return (-1); + + if (pid == 0) + pid = P_MYID; + + if (get_info_by_policy(policy) < 0) { + errno = EINVAL; + return (-1); + } + + switch (policy) { + case SCHED_FIFO: + case SCHED_RR: + if (prio < rt_class.pcc_primin || prio > rt_class.pcc_primax) { + errno = EINVAL; + return (-1); + } + pcparm.pc_cid = rt_class.pcc_info.pc_cid; + ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio; + ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs = + (policy == SCHED_RR ? RT_TQDEF : RT_TQINF); + break; + + case SCHED_OTHER: + pcparm.pc_cid = ts_class.pcc_info.pc_cid; + tsi = (tsinfo_t *)ts_class.pcc_info.pc_clinfo; + scale = tsi->ts_maxupri; + tsp = (tsparms_t *)pcparm.pc_clparms; + tsp->ts_uprilim = tsp->ts_upri = -(scale * prio) / 20; + break; + + default: + switch (policy) { + case SCHED_SYS: + pcparm.pc_cid = sys_class.pcc_info.pc_cid; + break; + case SCHED_IA: + pcparm.pc_cid = ia_class.pcc_info.pc_cid; + break; + default: + pcparm.pc_cid = policy - _SCHED_NEXT; + break; + } + /* + * policy is not defined by POSIX.4. + * just pass parameter data through to priocntl. + * param should contain an image of class-specific parameters + * (after the sched_priority member). + */ + *((pc_clparms_t *)pcparm.pc_clparms) = + *((pc_clparms_t *)&(param->sched_priority)+1); + } + + /* setting scheduling policy & parameters for the process */ + if (priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&pcparm) == -1) + return (-1); + + return (oldpolicy); +} + +int +sched_getscheduler(pid_t pid) +{ + pcparms_t pcparm; + int policy; + + if (pid < 0) { + errno = ESRCH; + return (-1); + } + if (pid == 0) + pid = P_MYID; + + /* get scheduling policy & parameters for the process */ + pcparm.pc_cid = PC_CLNULL; + if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) + return (-1); + if (get_info_by_class(pcparm.pc_cid) < 0) + return (-1); + + if (pcparm.pc_cid == rt_class.pcc_info.pc_cid) + policy = ((((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs == + RT_TQINF ? SCHED_FIFO : SCHED_RR)); + else if (pcparm.pc_cid == ts_class.pcc_info.pc_cid) + policy = SCHED_OTHER; + else if (pcparm.pc_cid == sys_class.pcc_info.pc_cid) + policy = SCHED_SYS; + else if (pcparm.pc_cid == ia_class.pcc_info.pc_cid) + policy = SCHED_IA; + else { + /* + * policy is not defined by POSIX.4 + * return a unique dot4 policy id. + */ + policy = (int)(_SCHED_NEXT + pcparm.pc_cid); + } + + return (policy); +} + +int +sched_yield(void) +{ + thr_yield(); + return (0); +} + +int +sched_get_priority_max(int policy) +{ + pcpri_t pcpri; + + if (get_info_by_policy(policy) < 0) + return (-1); + + if (policy == SCHED_FIFO || policy == SCHED_RR) + return (rt_class.pcc_primax); + else if (policy == SCHED_OTHER) + return (ts_class.pcc_primax); + else if (policy == SCHED_SYS) + return (sys_class.pcc_primax); + else if (policy == SCHED_IA) + return (ia_class.pcc_primax); + else { /* policy not in POSIX.4 */ + pcpri.pc_cid = policy - _SCHED_NEXT; + if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) + return (map_gp_to_rtpri(pcpri.pc_clpmax)); + } + + errno = EINVAL; + return (-1); +} + +int +sched_get_priority_min(int policy) +{ + pcpri_t pcpri; + + if (get_info_by_policy(policy) < 0) + return (-1); + + if (policy == SCHED_FIFO || policy == SCHED_RR) + return (rt_class.pcc_primin); + else if (policy == SCHED_OTHER) + return (ts_class.pcc_primin); + else if (policy == SCHED_SYS) + return (sys_class.pcc_primin); + else if (policy == SCHED_IA) + return (ia_class.pcc_primin); + else { /* policy not in POSIX.4 */ + pcpri.pc_cid = policy - _SCHED_NEXT; + if (priocntl(0, 0, PC_GETPRIRANGE, (caddr_t)&pcpri) == 0) + return (map_gp_to_rtpri(pcpri.pc_clpmin)); + } + + errno = EINVAL; + return (-1); +} + +int +sched_rr_get_interval(pid_t pid, timespec_t *interval) +{ + pcparms_t pcparm; + + if (pid < 0) { + errno = ESRCH; + return (-1); + } + if (pid == 0) + pid = P_MYID; + + if (get_info_by_policy(SCHED_RR) < 0) + return (-1); + + pcparm.pc_cid = PC_CLNULL; + if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&pcparm) == -1) + return (-1); + + if (pcparm.pc_cid == rt_class.pcc_info.pc_cid && + (((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF)) { + /* SCHED_RR */ + interval->tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs; + interval->tv_nsec = + ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs; + return (0); + } + + errno = EINVAL; + return (-1); +} |