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 /*
  23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 /*
  27  * Copyright 2018 Joyent, Inc.
  28  */
  29 
  30 #include "lint.h"
  31 #include "thr_uberdata.h"
  32 
  33 /*
  34  * pthread_once related data
  35  * This structure is exported as pthread_once_t in pthread.h.
  36  * We export only the size of this structure. so check
  37  * pthread_once_t in pthread.h before making a change here.
  38  */
  39 typedef struct  __once {
  40         mutex_t mlock;
  41         union {
  42                 uint32_t        pad32_flag[2];
  43                 uint64_t        pad64_flag;
  44         } oflag;
  45 } __once_t;
  46 
  47 #define once_flag       oflag.pad32_flag[1]
  48 
  49 static int
  50 _thr_setinherit(pthread_t tid, int inherit)
  51 {
  52         ulwp_t *ulwp;
  53         int error = 0;
  54 
  55         if ((ulwp = find_lwp(tid)) == NULL) {
  56                 error = ESRCH;
  57         } else {
  58                 ulwp->ul_ptinherit = inherit;
  59                 ulwp_unlock(ulwp, curthread->ul_uberdata);
  60         }
  61 
  62         return (error);
  63 }
  64 
  65 static int
  66 _thr_setparam(pthread_t tid, int policy, int prio)
  67 {
  68         ulwp_t *ulwp;
  69         id_t cid;
  70         int error = 0;
  71 
  72         if ((ulwp = find_lwp(tid)) == NULL) {
  73                 error = ESRCH;
  74         } else {
  75                 if (policy == ulwp->ul_policy &&
  76                     (policy == SCHED_FIFO || policy == SCHED_RR) &&
  77                     ulwp->ul_epri != 0) {
  78                         /*
  79                          * Don't change the ceiling priority,
  80                          * just the base priority.
  81                          */
  82                         if (prio > ulwp->ul_epri)
  83                                 error = EPERM;
  84                         else
  85                                 ulwp->ul_pri = prio;
  86                 } else if ((cid = setparam(P_LWPID, tid, policy, prio)) == -1) {
  87                         error = errno;
  88                 } else {
  89                         if (policy == SCHED_FIFO || policy == SCHED_RR)
  90                                 ulwp->ul_rtclassid = cid;
  91                         ulwp->ul_cid = cid;
  92                         ulwp->ul_pri = prio;
  93                         membar_producer();
  94                         ulwp->ul_policy = policy;
  95                 }
  96                 ulwp_unlock(ulwp, curthread->ul_uberdata);
  97         }
  98         return (error);
  99 }
 100 
 101 /*
 102  * pthread_create: creates a thread in the current process.
 103  * calls common _thrp_create() after copying the attributes.
 104  */
 105 #pragma weak _pthread_create = pthread_create
 106 int
 107 pthread_create(pthread_t *thread, const pthread_attr_t *attr,
 108     void * (*start_routine)(void *), void *arg)
 109 {
 110         ulwp_t          *self = curthread;
 111         const thrattr_t *ap = attr? attr->__pthread_attrp : def_thrattr();
 112         const pcclass_t *pccp;
 113         long            flag;
 114         pthread_t       tid;
 115         int             error;
 116 
 117         update_sched(self);
 118 
 119         if (ap == NULL)
 120                 return (EINVAL);
 121 
 122         /* validate explicit scheduling attributes */
 123         if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
 124             (ap->policy == SCHED_SYS ||
 125             (pccp = get_info_by_policy(ap->policy)) == NULL ||
 126             ap->prio < pccp->pcc_primin || ap->prio > pccp->pcc_primax))
 127                 return (EINVAL);
 128 
 129         flag = ap->scope | ap->detachstate | ap->daemonstate | THR_SUSPENDED;
 130         error = _thrp_create(ap->stkaddr, ap->stksize, start_routine, arg,
 131             flag, &tid, ap->guardsize, ap->name);
 132         if (error == 0) {
 133                 /*
 134                  * Record the original inheritence value for
 135                  * pthread_getattr_np(). We should always be able to find the
 136                  * thread.
 137                  */
 138                 (void) _thr_setinherit(tid, ap->inherit);
 139 
 140                 if (ap->inherit == PTHREAD_EXPLICIT_SCHED &&
 141                     (ap->policy != self->ul_policy ||
 142                     ap->prio != (self->ul_epri ? self->ul_epri :
 143                     self->ul_pri))) {
 144                         /*
 145                          * The SUSv3 specification requires pthread_create()
 146                          * to fail with EPERM if it cannot set the scheduling
 147                          * policy and parameters on the new thread.
 148                          */
 149                         error = _thr_setparam(tid, ap->policy, ap->prio);
 150                 }
 151 
 152                 if (error) {
 153                         /*
 154                          * We couldn't determine this error before
 155                          * actually creating the thread.  To recover,
 156                          * mark the thread detached and cancel it.
 157                          * It is as though it was never created.
 158                          */
 159                         ulwp_t *ulwp = find_lwp(tid);
 160                         if (ulwp->ul_detached == 0) {
 161                                 ulwp->ul_detached = 1;
 162                                 ulwp->ul_usropts |= THR_DETACHED;
 163                                 (void) __lwp_detach(tid);
 164                         }
 165                         ulwp->ul_cancel_pending = 2; /* cancelled on creation */
 166                         ulwp->ul_cancel_disabled = 0;
 167                         ulwp_unlock(ulwp, self->ul_uberdata);
 168                 } else if (thread) {
 169                         *thread = tid;
 170                 }
 171                 (void) thr_continue(tid);
 172         }
 173 
 174         /* posix version expects EAGAIN for lack of memory */
 175         if (error == ENOMEM)
 176                 error = EAGAIN;
 177         return (error);
 178 }
 179 
 180 /*
 181  * pthread_once: calls given function only once.
 182  * it synchronizes via mutex in pthread_once_t structure
 183  */
 184 int
 185 pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
 186 {
 187         __once_t *once = (__once_t *)once_control;
 188 
 189         if (once == NULL || init_routine == NULL)
 190                 return (EINVAL);
 191 
 192         if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
 193                 (void) mutex_lock(&once->mlock);
 194                 if (once->once_flag == PTHREAD_ONCE_NOTDONE) {
 195                         pthread_cleanup_push(mutex_unlock, &once->mlock);
 196                         (*init_routine)();
 197                         pthread_cleanup_pop(0);
 198                         membar_producer();
 199                         once->once_flag = PTHREAD_ONCE_DONE;
 200                 }
 201                 (void) mutex_unlock(&once->mlock);
 202         }
 203         membar_consumer();
 204 
 205         return (0);
 206 }
 207 
 208 /*
 209  * pthread_equal: equates two thread ids.
 210  */
 211 int
 212 pthread_equal(pthread_t t1, pthread_t t2)
 213 {
 214         return (t1 == t2);
 215 }
 216 
 217 /*
 218  * pthread_getschedparam: get the thread's sched parameters.
 219  */
 220 #pragma weak _pthread_getschedparam = pthread_getschedparam
 221 int
 222 pthread_getschedparam(pthread_t tid, int *policy, struct sched_param *param)
 223 {
 224         ulwp_t *ulwp;
 225         id_t cid;
 226         int error = 0;
 227 
 228         if ((ulwp = find_lwp(tid)) == NULL) {
 229                 error = ESRCH;
 230         } else {
 231                 cid = getparam(P_LWPID, ulwp->ul_lwpid, policy, param);
 232                 if (cid == -1) {
 233                         error = errno;
 234                 } else if (*policy == ulwp->ul_policy && cid == ulwp->ul_cid &&
 235                     (*policy == SCHED_FIFO || *policy == SCHED_RR)) {
 236                         /*
 237                          * Return the defined priority, not the effective
 238                          * priority from priority ceiling mutexes.
 239                          */
 240                         param->sched_priority = ulwp->ul_pri;
 241                 } else {
 242                         if (*policy == SCHED_FIFO || *policy == SCHED_RR)
 243                                 ulwp->ul_rtclassid = cid;
 244                         ulwp->ul_cid = cid;
 245                         ulwp->ul_pri = param->sched_priority;
 246                         membar_producer();
 247                         ulwp->ul_policy = *policy;
 248                 }
 249                 ulwp_unlock(ulwp, curthread->ul_uberdata);
 250         }
 251 
 252         return (error);
 253 }
 254 
 255 #pragma weak _thr_getprio = thr_getprio
 256 int
 257 thr_getprio(thread_t tid, int *priority)
 258 {
 259         struct sched_param param;
 260         int policy;
 261         int error;
 262 
 263         if ((error = pthread_getschedparam(tid, &policy, &param)) == 0)
 264                 *priority = param.sched_priority;
 265         return (error);
 266 }
 267 
 268 /*
 269  * pthread_setschedparam: sets the sched parameters for a thread.
 270  */
 271 int
 272 pthread_setschedparam(pthread_t tid,
 273     int policy, const struct sched_param *param)
 274 {
 275         return (_thr_setparam(tid, policy, param->sched_priority));
 276 }
 277 
 278 #pragma weak pthread_setschedprio = thr_setprio
 279 int
 280 thr_setprio(thread_t tid, int prio)
 281 {
 282         struct sched_param param;
 283         int policy;
 284         int error;
 285 
 286         /*
 287          * pthread_getschedparam() has the side-effect of setting
 288          * the target thread's ul_policy, ul_pri and ul_cid correctly.
 289          */
 290         if ((error = pthread_getschedparam(tid, &policy, &param)) != 0)
 291                 return (error);
 292         if (param.sched_priority == prio)       /* no change */
 293                 return (0);
 294         return (_thr_setparam(tid, policy, prio));
 295 }