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 /*
  28  * Copyright 2018, Joyent, Inc.
  29  */
  30 
  31 #include "lint.h"
  32 #include "thr_uberdata.h"
  33 #include <sys/ctype.h>
  34 #include <strings.h>
  35 #include <sched.h>
  36 
  37 /*
  38  * Default attribute object for pthread_create() with NULL attr pointer.
  39  * Note that the 'guardsize' field is initialized on the first call.
  40  */
  41 const thrattr_t *
  42 def_thrattr(void)
  43 {
  44         static thrattr_t thrattr = {
  45                 0,                              /* stksize */
  46                 NULL,                           /* stkaddr */
  47                 PTHREAD_CREATE_JOINABLE,        /* detachstate */
  48                 PTHREAD_CREATE_NONDAEMON_NP,    /* daemonstate */
  49                 PTHREAD_SCOPE_PROCESS,          /* scope */
  50                 0,                              /* prio */
  51                 SCHED_OTHER,                    /* policy */
  52                 PTHREAD_INHERIT_SCHED,          /* inherit */
  53                 0,                              /* guardsize */
  54                 { 0 }                           /* name */
  55         };
  56         if (thrattr.guardsize == 0)
  57                 thrattr.guardsize = _sysconf(_SC_PAGESIZE);
  58         return (&thrattr);
  59 }
  60 
  61 /*
  62  * pthread_attr_init: allocates the attribute object and initializes it
  63  * with the default values.
  64  */
  65 #pragma weak _pthread_attr_init = pthread_attr_init
  66 int
  67 pthread_attr_init(pthread_attr_t *attr)
  68 {
  69         thrattr_t *ap;
  70 
  71         if ((ap = lmalloc(sizeof (thrattr_t))) != NULL) {
  72                 *ap = *def_thrattr();
  73                 attr->__pthread_attrp = ap;
  74                 return (0);
  75         }
  76         return (ENOMEM);
  77 }
  78 
  79 /*
  80  * pthread_attr_destroy: frees the attribute object and invalidates it
  81  * with NULL value.
  82  */
  83 int
  84 pthread_attr_destroy(pthread_attr_t *attr)
  85 {
  86         if (attr == NULL || attr->__pthread_attrp == NULL)
  87                 return (EINVAL);
  88         lfree(attr->__pthread_attrp, sizeof (thrattr_t));
  89         attr->__pthread_attrp = NULL;
  90         return (0);
  91 }
  92 
  93 /*
  94  * pthread_attr_clone: make a copy of a pthread_attr_t.
  95  */
  96 int
  97 pthread_attr_clone(pthread_attr_t *attr, const pthread_attr_t *old_attr)
  98 {
  99         thrattr_t *ap;
 100         const thrattr_t *old_ap =
 101             old_attr ? old_attr->__pthread_attrp : def_thrattr();
 102 
 103         if (old_ap == NULL)
 104                 return (EINVAL);
 105         if ((ap = lmalloc(sizeof (thrattr_t))) == NULL)
 106                 return (ENOMEM);
 107         *ap = *old_ap;
 108         attr->__pthread_attrp = ap;
 109         return (0);
 110 }
 111 
 112 /*
 113  * pthread_attr_equal: compare two pthread_attr_t's, return 1 if equal.
 114  * A NULL pthread_attr_t pointer implies default attributes.
 115  * This is a consolidation-private interface, for librt.
 116  */
 117 int
 118 pthread_attr_equal(const pthread_attr_t *attr1, const pthread_attr_t *attr2)
 119 {
 120         const thrattr_t *ap1 = attr1 ? attr1->__pthread_attrp : def_thrattr();
 121         const thrattr_t *ap2 = attr2 ? attr2->__pthread_attrp : def_thrattr();
 122 
 123         if (ap1 == NULL || ap2 == NULL)
 124                 return (0);
 125         return (ap1 == ap2 || memcmp(ap1, ap2, sizeof (thrattr_t)) == 0);
 126 }
 127 
 128 /*
 129  * pthread_attr_setstacksize: sets the user stack size, minimum should
 130  * be PTHREAD_STACK_MIN (MINSTACK).
 131  * This is equivalent to stksize argument in thr_create().
 132  */
 133 int
 134 pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
 135 {
 136         thrattr_t *ap;
 137 
 138         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 139             stacksize >= MINSTACK) {
 140                 ap->stksize = stacksize;
 141                 return (0);
 142         }
 143         return (EINVAL);
 144 }
 145 
 146 /*
 147  * pthread_attr_getstacksize: gets the user stack size.
 148  */
 149 #pragma weak _pthread_attr_getstacksize = pthread_attr_getstacksize
 150 int
 151 pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
 152 {
 153         thrattr_t *ap;
 154 
 155         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 156             stacksize != NULL) {
 157                 *stacksize = ap->stksize;
 158                 return (0);
 159         }
 160         return (EINVAL);
 161 }
 162 
 163 /*
 164  * pthread_attr_setstackaddr: sets the user stack addr.
 165  * This is equivalent to stkaddr argument in thr_create().
 166  */
 167 int
 168 pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr)
 169 {
 170         thrattr_t *ap;
 171 
 172         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) {
 173                 ap->stkaddr = stackaddr;
 174                 return (0);
 175         }
 176         return (EINVAL);
 177 }
 178 
 179 /*
 180  * pthread_attr_getstackaddr: gets the user stack addr.
 181  */
 182 #pragma weak _pthread_attr_getstackaddr = pthread_attr_getstackaddr
 183 int
 184 pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr)
 185 {
 186         thrattr_t *ap;
 187 
 188         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 189             stackaddr != NULL) {
 190                 *stackaddr = ap->stkaddr;
 191                 return (0);
 192         }
 193         return (EINVAL);
 194 }
 195 
 196 /*
 197  * pthread_attr_setdetachstate: sets the detach state to DETACHED or JOINABLE.
 198  * PTHREAD_CREATE_DETACHED is equivalent to thr_create(THR_DETACHED).
 199  */
 200 int
 201 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
 202 {
 203         thrattr_t *ap;
 204 
 205         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 206             (detachstate == PTHREAD_CREATE_DETACHED ||
 207             detachstate == PTHREAD_CREATE_JOINABLE)) {
 208                 ap->detachstate = detachstate;
 209                 return (0);
 210         }
 211         return (EINVAL);
 212 }
 213 
 214 /*
 215  * pthread_attr_getdetachstate: gets the detach state.
 216  */
 217 #pragma weak _pthread_attr_getdetachstate = pthread_attr_getdetachstate
 218 int
 219 pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate)
 220 {
 221         thrattr_t *ap;
 222 
 223         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 224             detachstate != NULL) {
 225                 *detachstate = ap->detachstate;
 226                 return (0);
 227         }
 228         return (EINVAL);
 229 }
 230 
 231 /*
 232  * pthread_attr_setdaemonstate_np: sets the daemon state to DAEMON or NONDAEMON.
 233  * PTHREAD_CREATE_DAEMON is equivalent to thr_create(THR_DAEMON).
 234  * For now, this is a private interface in libc.
 235  */
 236 int
 237 pthread_attr_setdaemonstate_np(pthread_attr_t *attr, int daemonstate)
 238 {
 239         thrattr_t *ap;
 240 
 241         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 242             (daemonstate == PTHREAD_CREATE_DAEMON_NP ||
 243             daemonstate == PTHREAD_CREATE_NONDAEMON_NP)) {
 244                 ap->daemonstate = daemonstate;
 245                 return (0);
 246         }
 247         return (EINVAL);
 248 }
 249 
 250 /*
 251  * pthread_attr_getdaemonstate_np: gets the daemon state.
 252  * For now, this is a private interface in libc, but it is exposed in the
 253  * mapfile for the purposes of testing only.
 254  */
 255 int
 256 pthread_attr_getdaemonstate_np(const pthread_attr_t *attr, int *daemonstate)
 257 {
 258         thrattr_t *ap;
 259 
 260         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 261             daemonstate != NULL) {
 262                 *daemonstate = ap->daemonstate;
 263                 return (0);
 264         }
 265         return (EINVAL);
 266 }
 267 
 268 /*
 269  * pthread_attr_setscope: sets the scope to SYSTEM or PROCESS.
 270  * This is equivalent to setting THR_BOUND flag in thr_create().
 271  */
 272 int
 273 pthread_attr_setscope(pthread_attr_t *attr, int scope)
 274 {
 275         thrattr_t *ap;
 276 
 277         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 278             (scope == PTHREAD_SCOPE_SYSTEM ||
 279             scope == PTHREAD_SCOPE_PROCESS)) {
 280                 ap->scope = scope;
 281                 return (0);
 282         }
 283         return (EINVAL);
 284 }
 285 
 286 /*
 287  * pthread_attr_getscope: gets the scheduling scope.
 288  */
 289 #pragma weak _pthread_attr_getscope = pthread_attr_getscope
 290 int
 291 pthread_attr_getscope(const pthread_attr_t *attr, int *scope)
 292 {
 293         thrattr_t *ap;
 294 
 295         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 296             scope != NULL) {
 297                 *scope = ap->scope;
 298                 return (0);
 299         }
 300         return (EINVAL);
 301 }
 302 
 303 /*
 304  * pthread_attr_setinheritsched: sets the scheduling parameters to be
 305  * EXPLICIT or INHERITED from parent thread.
 306  */
 307 int
 308 pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit)
 309 {
 310         thrattr_t *ap;
 311 
 312         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 313             (inherit == PTHREAD_EXPLICIT_SCHED ||
 314             inherit == PTHREAD_INHERIT_SCHED)) {
 315                 ap->inherit = inherit;
 316                 return (0);
 317         }
 318         return (EINVAL);
 319 }
 320 
 321 /*
 322  * pthread_attr_getinheritsched: gets the scheduling inheritance.
 323  */
 324 #pragma weak _pthread_attr_getinheritsched = pthread_attr_getinheritsched
 325 int
 326 pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit)
 327 {
 328         thrattr_t *ap;
 329 
 330         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 331             inherit != NULL) {
 332                 *inherit = ap->inherit;
 333                 return (0);
 334         }
 335         return (EINVAL);
 336 }
 337 
 338 /*
 339  * pthread_attr_setschedpolicy: sets the scheduling policy.
 340  */
 341 int
 342 pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy)
 343 {
 344         thrattr_t *ap;
 345 
 346         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 347             policy != SCHED_SYS && get_info_by_policy(policy) != NULL) {
 348                 ap->policy = policy;
 349                 return (0);
 350         }
 351         return (EINVAL);
 352 }
 353 
 354 /*
 355  * pthread_attr_getpolicy: gets the scheduling policy.
 356  */
 357 #pragma weak _pthread_attr_getschedpolicy = pthread_attr_getschedpolicy
 358 int
 359 pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy)
 360 {
 361         thrattr_t *ap;
 362 
 363         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 364             policy != NULL) {
 365                 *policy = ap->policy;
 366                 return (0);
 367         }
 368         return (EINVAL);
 369 }
 370 
 371 /*
 372  * pthread_attr_setschedparam: sets the scheduling parameters.
 373  * Currently, we support priority only.
 374  */
 375 int
 376 pthread_attr_setschedparam(pthread_attr_t *attr,
 377     const struct sched_param *param)
 378 {
 379         thrattr_t *ap;
 380 
 381         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 382             param != NULL) {
 383                 ap->prio = param->sched_priority;
 384                 return (0);
 385         }
 386         return (EINVAL);
 387 }
 388 
 389 /*
 390  * pthread_attr_getschedparam: gets the scheduling parameters.
 391  * Currently, only priority is defined as sched parameter.
 392  */
 393 #pragma weak _pthread_attr_getschedparam = pthread_attr_getschedparam
 394 int
 395 pthread_attr_getschedparam(const pthread_attr_t *attr,
 396     struct sched_param *param)
 397 {
 398         thrattr_t *ap;
 399 
 400         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 401             param != NULL) {
 402                 param->sched_priority = ap->prio;
 403                 return (0);
 404         }
 405         return (EINVAL);
 406 }
 407 
 408 /*
 409  * UNIX98
 410  * pthread_attr_setguardsize: sets the guardsize
 411  */
 412 int
 413 pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)
 414 {
 415         thrattr_t *ap;
 416 
 417         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL) {
 418                 ap->guardsize = guardsize;
 419                 return (0);
 420         }
 421         return (EINVAL);
 422 }
 423 
 424 /*
 425  * UNIX98
 426  * pthread_attr_getguardsize: gets the guardsize
 427  */
 428 int
 429 pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize)
 430 {
 431         thrattr_t *ap;
 432 
 433         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 434             guardsize != NULL) {
 435                 *guardsize = ap->guardsize;
 436                 return (0);
 437         }
 438         return (EINVAL);
 439 }
 440 
 441 /*
 442  * pthread_attr_setstack: sets the user stack addr and stack size.
 443  * This is equivalent to the stack_base and stack_size arguments
 444  * to thr_create().
 445  */
 446 int
 447 pthread_attr_setstack(pthread_attr_t *attr,
 448     void *stackaddr, size_t stacksize)
 449 {
 450         thrattr_t *ap;
 451 
 452         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 453             stacksize >= MINSTACK) {
 454                 ap->stkaddr = stackaddr;
 455                 ap->stksize = stacksize;
 456                 if (stackaddr != NULL &&
 457                     setup_top_frame(stackaddr, stacksize, NULL) == NULL)
 458                         return (EACCES);
 459                 return (0);
 460         }
 461         return (EINVAL);
 462 }
 463 
 464 /*
 465  * pthread_attr_getstack: gets the user stack addr and stack size.
 466  */
 467 int
 468 pthread_attr_getstack(const pthread_attr_t *attr,
 469     void **stackaddr, size_t *stacksize)
 470 {
 471         thrattr_t *ap;
 472 
 473         if (attr != NULL && (ap = attr->__pthread_attrp) != NULL &&
 474             stackaddr != NULL && stacksize != NULL) {
 475                 *stackaddr = ap->stkaddr;
 476                 *stacksize = ap->stksize;
 477                 return (0);
 478         }
 479         return (EINVAL);
 480 }
 481 
 482 int
 483 pthread_attr_setname_np(pthread_attr_t *attr, const char *name)
 484 {
 485         thrattr_t *ap;
 486 
 487         if (attr == NULL || (ap = attr->__pthread_attrp) == NULL)
 488                 return (EINVAL);
 489 
 490         if (name == NULL) {
 491                 bzero(ap->name, sizeof (ap->name));
 492                 return (0);
 493         }
 494 
 495         if (strlen(name) >= sizeof (ap->name))
 496                 return (ERANGE);
 497 
 498         /*
 499          * We really want the ASCII version of isprint() here...
 500          */
 501         for (size_t i = 0; name[i] != '\0'; i++) {
 502                 if (!ISPRINT(name[i]))
 503                         return (EINVAL);
 504         }
 505 
 506         /*
 507          * not having garbage after the end of the string simplifies attr
 508          * comparison
 509          */
 510         bzero(ap->name, sizeof (ap->name));
 511         (void) strlcpy(ap->name, name, sizeof (ap->name));
 512         return (0);
 513 }
 514 
 515 int
 516 pthread_attr_getname_np(pthread_attr_t *attr, char *buf, size_t len)
 517 {
 518         thrattr_t *ap;
 519 
 520         if (buf == NULL || attr == NULL ||
 521             (ap = attr->__pthread_attrp) == NULL)
 522                 return (EINVAL);
 523 
 524         if (strlcpy(buf, ap->name, len) > len)
 525                 return (ERANGE);
 526         return (0);
 527 }
 528 
 529 /*
 530  * This function is a common BSD extension to pthread which is used to obtain
 531  * the attributes of a thread that might have changed after its creation, for
 532  * example, it's stack address.
 533  *
 534  * Note, there is no setattr analogue, nor do we desire to add one at this time.
 535  * Similarly there is no native threads API analogue (nor should we add one for
 536  * C11).
 537  *
 538  * The astute reader may note that there is a GNU version of this called
 539  * pthread_getattr_np(). The two functions are similar, but subtley different in
 540  * a rather important way. While the pthread_attr_get_np() expects to be given
 541  * a pthread_attr_t that has had pthread_attr_init() called on in,
 542  * pthread_getattr_np() does not. However, on GNU systems, where the function
 543  * originates, the pthread_attr_t is not opaque and thus it is entirely safe to
 544  * both call pthread_attr_init() and then call pthread_getattr_np() on the same
 545  * attributes object. On illumos, since the pthread_attr_t is opaque, that would
 546  * be a memory leak. As such, we don't provide it.
 547  */
 548 int
 549 pthread_attr_get_np(pthread_t tid, pthread_attr_t *attr)
 550 {
 551         int ret;
 552         ulwp_t *self = curthread;
 553         uberdata_t *udp = self->ul_uberdata;
 554         ulwp_t *target = NULL;
 555         thrattr_t *ap;
 556 
 557         /*
 558          * To ensure that information about the target thread does not change or
 559          * disappear while we're trying to interrogate it, we grab the uwlp
 560          * lock.
 561          */
 562         if (self->ul_lwpid == tid) {
 563                 ulwp_lock(self, udp);
 564                 target = self;
 565         } else {
 566                 target = find_lwp(tid);
 567                 if (target == NULL)
 568                         return (ESRCH);
 569         }
 570 
 571         if (attr == NULL) {
 572                 ret = EINVAL;
 573                 goto out;
 574         }
 575 
 576         if ((ap = attr->__pthread_attrp) == NULL) {
 577                 ret = EINVAL;
 578                 goto out;
 579         }
 580 
 581         ap->stksize = target->ul_stksiz;
 582         ap->stkaddr = target->ul_stk;
 583         if (target->ul_usropts & THR_DETACHED) {
 584                 ap->detachstate = PTHREAD_CREATE_DETACHED;
 585         } else {
 586                 ap->detachstate = PTHREAD_CREATE_JOINABLE;
 587         }
 588 
 589         if (target->ul_usropts & THR_DAEMON) {
 590                 ap->daemonstate = PTHREAD_CREATE_DAEMON_NP;
 591         } else {
 592                 ap->daemonstate = PTHREAD_CREATE_NONDAEMON_NP;
 593         }
 594 
 595         if (target->ul_usropts & THR_BOUND) {
 596                 ap->scope = PTHREAD_SCOPE_SYSTEM;
 597         } else {
 598                 ap->scope = PTHREAD_SCOPE_PROCESS;
 599         }
 600         ap->prio = target->ul_pri;
 601         ap->policy = target->ul_policy;
 602         ap->inherit = target->ul_ptinherit;
 603         ap->guardsize = target->ul_guardsize;
 604         (void) pthread_getname_np(tid, ap->name, sizeof (ap->name));
 605 
 606         ret = 0;
 607 out:
 608         ulwp_unlock(target, udp);
 609         return (ret);
 610 }