1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <sys/types.h>
  29 #include <sys/systm.h>
  30 #include <sys/errno.h>
  31 #include <sys/proc.h>
  32 #include <sys/cpu.h>
  33 #include <sys/rtpriocntl.h>
  34 #include <sys/tspriocntl.h>
  35 #include <sys/processor.h>
  36 #include <sys/brand.h>
  37 #include <sys/lx_pid.h>
  38 #include <sys/lx_sched.h>
  39 #include <sys/lx_brand.h>
  40 
  41 extern long priocntl_common(int, procset_t *, int, caddr_t, caddr_t, uio_seg_t);
  42 
  43 int
  44 lx_sched_affinity(int cmd, uintptr_t pid, int len, uintptr_t maskp,
  45     int64_t *rval)
  46 {
  47         pid_t           s_pid;
  48         id_t            s_tid;
  49         kthread_t       *t = curthread;
  50         lx_lwp_data_t   *lx_lwp;
  51 
  52         if (cmd != B_GET_AFFINITY_MASK && cmd != B_SET_AFFINITY_MASK)
  53                 return (set_errno(EINVAL));
  54 
  55         /*
  56          * The caller wants to know how large the mask should be.
  57          */
  58         if (cmd == B_GET_AFFINITY_MASK && len == 0) {
  59                 *rval = sizeof (lx_affmask_t);
  60                 return (0);
  61         }
  62 
  63         /*
  64          * Otherwise, ensure they have a large enough mask.
  65          */
  66         if (cmd == B_GET_AFFINITY_MASK && len < sizeof (lx_affmask_t)) {
  67                 *rval = -1;
  68                 return (set_errno(EINVAL));
  69         }
  70 
  71         if (pid == 0) {
  72                 s_pid = curproc->p_pid;
  73                 s_tid = curthread->t_tid;
  74         } else if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) == -1) {
  75                 return (set_errno(ESRCH));
  76         }
  77 
  78         /*
  79          * For now, we only support manipulating threads in the
  80          * same process.
  81          */
  82         if (curproc->p_pid != s_pid)
  83                 return (set_errno(EPERM));
  84 
  85         /*
  86          * We must hold the process lock so that the thread list
  87          * doesn't change while we're looking at it. We'll hold
  88          * the lock until we no longer reference the
  89          * corresponding lwp.
  90          */
  91 
  92         mutex_enter(&curproc->p_lock);
  93 
  94         do {
  95                 if (t->t_tid == s_tid)
  96                         break;
  97                 t = t->t_forw;
  98         } while (t != curthread);
  99 
 100         /*
 101          * If the given PID is in the current thread's process,
 102          * then we _must_ find it in the process's thread list.
 103          */
 104         ASSERT(t->t_tid == s_tid);
 105 
 106         lx_lwp = t->t_lwp->lwp_brand;
 107 
 108         if (cmd == B_SET_AFFINITY_MASK) {
 109                 if (copyin_nowatch((void *)maskp, &lx_lwp->br_affinitymask,
 110                     sizeof (lx_affmask_t)) != 0) {
 111                         mutex_exit(&curproc->p_lock);
 112                         return (set_errno(EFAULT));
 113                 }
 114 
 115                 *rval = 0;
 116         } else {
 117                 if (copyout_nowatch(&lx_lwp->br_affinitymask, (void *)maskp,
 118                     sizeof (lx_affmask_t)) != 0) {
 119                         mutex_exit(&curproc->p_lock);
 120                         return (set_errno(EFAULT));
 121                 }
 122 
 123                 *rval = sizeof (lx_affmask_t);
 124         }
 125 
 126         mutex_exit(&curproc->p_lock);
 127         return (0);
 128 }
 129 
 130 long
 131 lx_sched_setscheduler(l_pid_t pid, int policy, struct lx_sched_param *param)
 132 {
 133         klwp_t *lwp = ttolwp(curthread);
 134         procset_t procset;
 135         procset_t procset_cid;
 136         pcparms_t pcparm;
 137         pcinfo_t pcinfo;
 138         struct lx_sched_param sched_param;
 139         tsparms_t *tsp;
 140         int prio, maxupri;
 141         int rv;
 142 
 143         if (pid < 0)
 144                 return (set_errno(ESRCH));
 145 
 146         if ((rv = sched_setprocset(&procset, pid)))
 147                 return (rv);
 148 
 149         if (copyin(param, &sched_param, sizeof (sched_param)))
 150                 return (set_errno(EFAULT));
 151 
 152         prio = sched_param.lx_sched_prio;
 153 
 154         if (policy < 0) {
 155                 /*
 156                  * get the class id
 157                  */
 158                 pcparm.pc_cid = PC_CLNULL;
 159                 (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
 160                 if (lwp->lwp_errno)
 161                         return (lwp->lwp_errno);
 162 
 163                 /*
 164                  * get the current policy
 165                  */
 166                 bzero(&pcinfo, sizeof (pcinfo));
 167                 pcinfo.pc_cid = pcparm.pc_cid;
 168                 (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
 169                 if (lwp->lwp_errno)
 170                         return (lwp->lwp_errno);
 171 
 172                 if (strcmp(pcinfo.pc_clname, "TS") == 0)
 173                         policy = LX_SCHED_OTHER;
 174                 else if (strcmp(pcinfo.pc_clname, "RT") == 0)
 175                         policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
 176                                 RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
 177                 else
 178                         return (set_errno(EINVAL));
 179         }
 180 
 181         bzero(&pcinfo, sizeof (pcinfo));
 182         bzero(&pcparm, sizeof (pcparm));
 183         setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
 184         switch (policy) {
 185         case LX_SCHED_FIFO:
 186         case LX_SCHED_RR:
 187                 (void) strcpy(pcinfo.pc_clname, "RT");
 188                 (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
 189                 if (lwp->lwp_errno)
 190                         return (lwp->lwp_errno);
 191 
 192                 if (prio < 0 ||
 193                     prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
 194                         return (set_errno(EINVAL));
 195                 pcparm.pc_cid = pcinfo.pc_cid;
 196                 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
 197                 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
 198                         policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
 199                 break;
 200 
 201         case LX_SCHED_OTHER:
 202                 (void) strcpy(pcinfo.pc_clname, "TS");
 203                 (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
 204                 if (lwp->lwp_errno)
 205                         return (lwp->lwp_errno);
 206 
 207                 maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
 208                 if (prio > maxupri || prio < -maxupri)
 209                         return (set_errno(EINVAL));
 210 
 211                 pcparm.pc_cid = pcinfo.pc_cid;
 212                 tsp = (tsparms_t *)pcparm.pc_clparms;
 213                 tsp->ts_upri = prio;
 214                 tsp->ts_uprilim = TS_NOCHANGE;
 215                 break;
 216 
 217         default:
 218                 return (set_errno(EINVAL));
 219         }
 220 
 221         /*
 222          * finally set scheduling policy and parameters
 223          */
 224         (void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
 225 
 226         return (0);
 227 }
 228 
 229 long
 230 lx_sched_getscheduler(l_pid_t pid)
 231 {
 232         klwp_t *lwp = ttolwp(curthread);
 233         procset_t procset;
 234         pcparms_t pcparm;
 235         pcinfo_t pcinfo;
 236         int policy;
 237         int rv;
 238 
 239         if (pid < 0)
 240                 return (set_errno(ESRCH));
 241 
 242         if ((rv = sched_setprocset(&procset, pid)))
 243                 return (rv);
 244 
 245         /*
 246          * get the class id
 247          */
 248         pcparm.pc_cid = PC_CLNULL;
 249         (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
 250         if (lwp->lwp_errno)
 251                 return (lwp->lwp_errno);
 252 
 253         /*
 254          * get the class info and identify the equivalent linux policy
 255          */
 256         bzero(&pcinfo, sizeof (pcinfo));
 257         pcinfo.pc_cid = pcparm.pc_cid;
 258         (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
 259         if (lwp->lwp_errno)
 260                 return (lwp->lwp_errno);
 261 
 262         if (strcmp(pcinfo.pc_clname, "TS") == 0)
 263                 policy = LX_SCHED_OTHER;
 264         else if (strcmp(pcinfo.pc_clname, "RT") == 0)
 265                 policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
 266                         RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
 267         else
 268                 policy = set_errno(EINVAL);
 269 
 270         return (policy);
 271 }
 272 
 273 long
 274 lx_sched_setparam(l_pid_t pid, struct lx_sched_param *param)
 275 {
 276         klwp_t *lwp = ttolwp(curthread);
 277         procset_t procset;
 278         procset_t procset_cid;
 279         pcparms_t pcparm;
 280         pcinfo_t pcinfo;
 281         struct lx_sched_param sched_param;
 282         tsparms_t *tsp;
 283         int policy;
 284         int prio, maxupri;
 285         int rv;
 286 
 287         if (pid < 0)
 288                 return (set_errno(ESRCH));
 289 
 290         if ((rv = sched_setprocset(&procset, pid)))
 291                 return (rv);
 292 
 293         if (copyin(param, &sched_param, sizeof (sched_param)))
 294                 return (set_errno(EFAULT));
 295 
 296         prio = sched_param.lx_sched_prio;
 297 
 298         /*
 299          * get the class id
 300          */
 301         pcparm.pc_cid = PC_CLNULL;
 302         (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
 303         if (lwp->lwp_errno)
 304                 return (lwp->lwp_errno);
 305 
 306         /*
 307          * get the current policy
 308          */
 309         bzero(&pcinfo, sizeof (pcinfo));
 310         pcinfo.pc_cid = pcparm.pc_cid;
 311         (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
 312         if (lwp->lwp_errno)
 313                 return (lwp->lwp_errno);
 314 
 315         if (strcmp(pcinfo.pc_clname, "TS") == 0)
 316                 policy = LX_SCHED_OTHER;
 317         else if (strcmp(pcinfo.pc_clname, "RT") == 0)
 318                 policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
 319                         RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
 320         else
 321                 return (set_errno(EINVAL));
 322 
 323         bzero(&pcinfo, sizeof (pcinfo));
 324         bzero(&pcparm, sizeof (pcparm));
 325         setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
 326         switch (policy) {
 327         case LX_SCHED_FIFO:
 328         case LX_SCHED_RR:
 329                 (void) strcpy(pcinfo.pc_clname, "RT");
 330                 (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
 331                 if (lwp->lwp_errno)
 332                         return (lwp->lwp_errno);
 333 
 334                 if (prio < 0 ||
 335                     prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
 336                         return (set_errno(EINVAL));
 337                 pcparm.pc_cid = pcinfo.pc_cid;
 338                 ((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
 339                 ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
 340                         policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
 341                 break;
 342 
 343         case LX_SCHED_OTHER:
 344                 (void) strcpy(pcinfo.pc_clname, "TS");
 345                 (void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
 346                 if (lwp->lwp_errno)
 347                         return (lwp->lwp_errno);
 348 
 349                 maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
 350                 if (prio > maxupri || prio < -maxupri)
 351                         return (set_errno(EINVAL));
 352 
 353                 pcparm.pc_cid = pcinfo.pc_cid;
 354                 tsp = (tsparms_t *)pcparm.pc_clparms;
 355                 tsp->ts_upri = prio;
 356                 tsp->ts_uprilim = TS_NOCHANGE;
 357                 break;
 358 
 359         default:
 360                 return (set_errno(EINVAL));
 361         }
 362 
 363         /*
 364          * finally set scheduling policy and parameters
 365          */
 366         (void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
 367 
 368         return (0);
 369 }
 370 
 371 long
 372 lx_sched_getparam(l_pid_t pid, struct lx_sched_param *param)
 373 {
 374         klwp_t *lwp = ttolwp(curthread);
 375         struct lx_sched_param local_param;
 376         procset_t procset;
 377         pcparms_t pcparm;
 378         pcinfo_t pcinfo;
 379         tsinfo_t *tsi;
 380         int prio, scale;
 381         int rv;
 382 
 383         if (pid < 0)
 384                 return (set_errno(ESRCH));
 385 
 386         if ((rv = sched_setprocset(&procset, pid)))
 387                 return (rv);
 388 
 389         /*
 390          * get the class id
 391          */
 392         pcparm.pc_cid = PC_CLNULL;
 393         (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
 394         if (lwp->lwp_errno)
 395                 return (lwp->lwp_errno);
 396 
 397         /*
 398          * get the class info and identify the equivalent linux policy
 399          */
 400         bzero(&pcinfo, sizeof (pcinfo));
 401         pcinfo.pc_cid = pcparm.pc_cid;
 402         (void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
 403         if (lwp->lwp_errno)
 404                 return (lwp->lwp_errno);
 405 
 406         bzero(&local_param, sizeof (local_param));
 407         if (strcmp(pcinfo.pc_clname, "TS") == 0) {
 408                 /*
 409                  * I don't know if we need to do this, coz it can't be
 410                  * changed from zero anyway.....
 411                  */
 412                 tsi = (tsinfo_t *)pcinfo.pc_clinfo;
 413                 prio = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
 414                 scale = tsi->ts_maxupri;
 415                 if (scale == 0)
 416                         local_param.lx_sched_prio = 0;
 417                 else
 418                         local_param.lx_sched_prio = -(prio * 20) / scale;
 419         } else if (strcmp(pcinfo.pc_clname, "RT") == 0)
 420                 local_param.lx_sched_prio =
 421                         ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
 422         else
 423                 rv = set_errno(EINVAL);
 424 
 425         if (rv == 0)
 426                 if (copyout(&local_param, param, sizeof (local_param)))
 427                         return (set_errno(EFAULT));
 428 
 429         return (rv);
 430 }
 431 
 432 long
 433 lx_sched_rr_get_interval(l_pid_t pid, struct timespec *ival)
 434 {
 435         klwp_t *lwp = ttolwp(curthread);
 436         struct timespec interval;
 437         procset_t procset;
 438         pcparms_t pcparm;
 439         pcinfo_t pcinfo;
 440         int rv;
 441 
 442         if (pid < 0)
 443                 return (set_errno(ESRCH));
 444 
 445         if ((rv = sched_setprocset(&procset, pid)))
 446                 return (rv);
 447 
 448         /*
 449          * get the class id
 450          */
 451         pcparm.pc_cid = PC_CLNULL;
 452         (void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
 453         if (lwp->lwp_errno)
 454                 return (lwp->lwp_errno);
 455 
 456         /*
 457          * get the class info and identify the equivalent linux policy
 458          */
 459         setprocset(&procset, POP_AND, P_PID, 0, P_ALL, 0);
 460         bzero(&pcinfo, sizeof (pcinfo));
 461         (void) strcpy(pcinfo.pc_clname, "RT");
 462         (void) do_priocntlsys(PC_GETCID, &procset, &pcinfo);
 463         if (lwp->lwp_errno)
 464                 return (lwp->lwp_errno);
 465 
 466         if (pcparm.pc_cid == pcinfo.pc_cid &&
 467             ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF) {
 468                 interval.tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs;
 469                 interval.tv_nsec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs;
 470 
 471                 if (copyout(&interval, ival, sizeof (interval)))
 472                         return (set_errno(EFAULT));
 473 
 474                 return (0);
 475         }
 476 
 477         return (set_errno(EINVAL));
 478 }
 479 
 480 int
 481 sched_setprocset(procset_t *procset, l_pid_t pid)
 482 {
 483         id_t lid, rid;
 484         idtype_t lidtype, ridtype;
 485 
 486         /*
 487          * define the target lwp
 488          */
 489         if (pid == 0) {
 490                 ridtype = P_ALL;
 491                 lidtype = P_PID;
 492                 rid = 0;
 493                 lid = P_MYID;
 494         } else {
 495                 if (lx_lpid_to_spair(pid, &pid, &lid) < 0)
 496                         return (set_errno(ESRCH));
 497                 if (pid != curproc->p_pid)
 498                         return (set_errno(ESRCH));
 499                 rid = 0;
 500                 ridtype = P_ALL;
 501                 lidtype = P_LWPID;
 502         }
 503         setprocset(procset, POP_AND, lidtype, lid, ridtype, rid);
 504 
 505         return (0);
 506 }
 507 
 508 long
 509 do_priocntlsys(int cmd, procset_t *procset, void *arg)
 510 {
 511         return (priocntl_common(PC_VERSION, procset, cmd, (caddr_t)arg, 0,
 512             UIO_SYSSPACE));
 513 }