kern_umtx.c revision 161855
1139804Simp/*- 2139013Sdavidxu * Copyright (c) 2004, David Xu <davidxu@freebsd.org> 3112904Sjeff * Copyright (c) 2002, Jeffrey Roberson <jeff@freebsd.org> 4112904Sjeff * All rights reserved. 5112904Sjeff * 6112904Sjeff * Redistribution and use in source and binary forms, with or without 7112904Sjeff * modification, are permitted provided that the following conditions 8112904Sjeff * are met: 9112904Sjeff * 1. Redistributions of source code must retain the above copyright 10112904Sjeff * notice unmodified, this list of conditions, and the following 11112904Sjeff * disclaimer. 12112904Sjeff * 2. Redistributions in binary form must reproduce the above copyright 13112904Sjeff * notice, this list of conditions and the following disclaimer in the 14112904Sjeff * documentation and/or other materials provided with the distribution. 15112904Sjeff * 16112904Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17112904Sjeff * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18112904Sjeff * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19112904Sjeff * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20112904Sjeff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21112904Sjeff * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22112904Sjeff * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23112904Sjeff * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24112904Sjeff * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25112904Sjeff * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26112904Sjeff */ 27112904Sjeff 28116182Sobrien#include <sys/cdefs.h> 29116182Sobrien__FBSDID("$FreeBSD: head/sys/kern/kern_umtx.c 161855 2006-09-02 02:41:33Z davidxu $"); 30116182Sobrien 31112904Sjeff#include <sys/param.h> 32112904Sjeff#include <sys/kernel.h> 33131431Smarcel#include <sys/limits.h> 34112904Sjeff#include <sys/lock.h> 35115765Sjeff#include <sys/malloc.h> 36112904Sjeff#include <sys/mutex.h> 37112904Sjeff#include <sys/proc.h> 38161678Sdavidxu#include <sys/sched.h> 39161678Sdavidxu#include <sys/sysctl.h> 40112904Sjeff#include <sys/sysent.h> 41112904Sjeff#include <sys/systm.h> 42112904Sjeff#include <sys/sysproto.h> 43139013Sdavidxu#include <sys/eventhandler.h> 44112904Sjeff#include <sys/umtx.h> 45112904Sjeff 46139013Sdavidxu#include <vm/vm.h> 47139013Sdavidxu#include <vm/vm_param.h> 48139013Sdavidxu#include <vm/pmap.h> 49139013Sdavidxu#include <vm/vm_map.h> 50139013Sdavidxu#include <vm/vm_object.h> 51139013Sdavidxu 52161678Sdavidxu#define TYPE_SIMPLE_LOCK 0 53161678Sdavidxu#define TYPE_SIMPLE_WAIT 1 54161678Sdavidxu#define TYPE_NORMAL_UMUTEX 2 55161678Sdavidxu#define TYPE_PI_UMUTEX 3 56161678Sdavidxu#define TYPE_PP_UMUTEX 4 57161678Sdavidxu#define TYPE_CV 5 58139013Sdavidxu 59161678Sdavidxu/* Key to represent a unique userland synchronous object */ 60139013Sdavidxustruct umtx_key { 61161678Sdavidxu int hash; 62139013Sdavidxu int type; 63161678Sdavidxu int shared; 64139013Sdavidxu union { 65139013Sdavidxu struct { 66139013Sdavidxu vm_object_t object; 67161678Sdavidxu uintptr_t offset; 68139013Sdavidxu } shared; 69139013Sdavidxu struct { 70161678Sdavidxu struct vmspace *vs; 71161678Sdavidxu uintptr_t addr; 72139013Sdavidxu } private; 73139013Sdavidxu struct { 74161678Sdavidxu void *a; 75161678Sdavidxu uintptr_t b; 76139013Sdavidxu } both; 77139013Sdavidxu } info; 78139013Sdavidxu}; 79139013Sdavidxu 80161678Sdavidxu/* Priority inheritance mutex info. */ 81161678Sdavidxustruct umtx_pi { 82161678Sdavidxu /* Owner thread */ 83161678Sdavidxu struct thread *pi_owner; 84161678Sdavidxu 85161678Sdavidxu /* Reference count */ 86161678Sdavidxu int pi_refcount; 87161678Sdavidxu 88161678Sdavidxu /* List entry to link umtx holding by thread */ 89161678Sdavidxu TAILQ_ENTRY(umtx_pi) pi_link; 90161678Sdavidxu 91161678Sdavidxu /* List entry in hash */ 92161678Sdavidxu TAILQ_ENTRY(umtx_pi) pi_hashlink; 93161678Sdavidxu 94161678Sdavidxu /* List for waiters */ 95161678Sdavidxu TAILQ_HEAD(,umtx_q) pi_blocked; 96161678Sdavidxu 97161678Sdavidxu /* Identify a userland lock object */ 98161678Sdavidxu struct umtx_key pi_key; 99161678Sdavidxu}; 100161678Sdavidxu 101161678Sdavidxu/* A userland synchronous object user. */ 102115765Sjeffstruct umtx_q { 103161678Sdavidxu /* Linked list for the hash. */ 104161678Sdavidxu TAILQ_ENTRY(umtx_q) uq_link; 105161678Sdavidxu 106161678Sdavidxu /* Umtx key. */ 107161678Sdavidxu struct umtx_key uq_key; 108161678Sdavidxu 109161678Sdavidxu /* Umtx flags. */ 110161678Sdavidxu int uq_flags; 111161678Sdavidxu#define UQF_UMTXQ 0x0001 112161678Sdavidxu 113161678Sdavidxu /* The thread waits on. */ 114161678Sdavidxu struct thread *uq_thread; 115161678Sdavidxu 116161678Sdavidxu /* 117161678Sdavidxu * Blocked on PI mutex. read can use chain lock 118161678Sdavidxu * or sched_lock, write must have both chain lock and 119161678Sdavidxu * sched_lock being hold. 120161678Sdavidxu */ 121161678Sdavidxu struct umtx_pi *uq_pi_blocked; 122161678Sdavidxu 123161678Sdavidxu /* On blocked list */ 124161678Sdavidxu TAILQ_ENTRY(umtx_q) uq_lockq; 125161678Sdavidxu 126161678Sdavidxu /* Thread contending with us */ 127161678Sdavidxu TAILQ_HEAD(,umtx_pi) uq_pi_contested; 128161678Sdavidxu 129161742Sdavidxu /* Inherited priority from PP mutex */ 130161678Sdavidxu u_char uq_inherited_pri; 131115765Sjeff}; 132115765Sjeff 133161678SdavidxuTAILQ_HEAD(umtxq_head, umtx_q); 134161678Sdavidxu 135161678Sdavidxu/* Userland lock object's wait-queue chain */ 136138224Sdavidxustruct umtxq_chain { 137161678Sdavidxu /* Lock for this chain. */ 138161678Sdavidxu struct mtx uc_lock; 139161678Sdavidxu 140161678Sdavidxu /* List of sleep queues. */ 141161678Sdavidxu struct umtxq_head uc_queue; 142161678Sdavidxu 143161678Sdavidxu /* Busy flag */ 144161678Sdavidxu char uc_busy; 145161678Sdavidxu 146161678Sdavidxu /* Chain lock waiters */ 147158377Sdavidxu int uc_waiters; 148161678Sdavidxu 149161678Sdavidxu /* All PI in the list */ 150161678Sdavidxu TAILQ_HEAD(,umtx_pi) uc_pi_list; 151138224Sdavidxu}; 152115765Sjeff 153161678Sdavidxu#define UMTXQ_LOCKED_ASSERT(uc) mtx_assert(&(uc)->uc_lock, MA_OWNED) 154161678Sdavidxu 155161678Sdavidxu/* 156161678Sdavidxu * Don't propagate time-sharing priority, there is a security reason, 157161678Sdavidxu * a user can simply introduce PI-mutex, let thread A lock the mutex, 158161678Sdavidxu * and let another thread B block on the mutex, because B is 159161678Sdavidxu * sleeping, its priority will be boosted, this causes A's priority to 160161678Sdavidxu * be boosted via priority propagating too and will never be lowered even 161161678Sdavidxu * if it is using 100%CPU, this is unfair to other processes. 162161678Sdavidxu */ 163161678Sdavidxu 164161678Sdavidxu#define UPRI(td) (((td)->td_ksegrp->kg_user_pri >= PRI_MIN_TIMESHARE &&\ 165161678Sdavidxu (td)->td_ksegrp->kg_user_pri <= PRI_MAX_TIMESHARE) ?\ 166161678Sdavidxu PRI_MAX_TIMESHARE : (td)->td_ksegrp->kg_user_pri) 167161678Sdavidxu 168138224Sdavidxu#define GOLDEN_RATIO_PRIME 2654404609U 169138224Sdavidxu#define UMTX_CHAINS 128 170138224Sdavidxu#define UMTX_SHIFTS (__WORD_BIT - 7) 171115765Sjeff 172161678Sdavidxu#define THREAD_SHARE 0 173161678Sdavidxu#define PROCESS_SHARE 1 174161678Sdavidxu#define AUTO_SHARE 2 175161678Sdavidxu 176161678Sdavidxu#define GET_SHARE(flags) \ 177161678Sdavidxu (((flags) & USYNC_PROCESS_SHARED) == 0 ? THREAD_SHARE : PROCESS_SHARE) 178161678Sdavidxu 179161678Sdavidxustatic uma_zone_t umtx_pi_zone; 180161678Sdavidxustatic struct umtxq_chain umtxq_chains[UMTX_CHAINS]; 181138224Sdavidxustatic MALLOC_DEFINE(M_UMTX, "umtx", "UMTX queue memory"); 182161678Sdavidxustatic int umtx_pi_allocated; 183115310Sjeff 184161678SdavidxuSYSCTL_NODE(_debug, OID_AUTO, umtx, CTLFLAG_RW, 0, "umtx debug"); 185161678SdavidxuSYSCTL_INT(_debug_umtx, OID_AUTO, umtx_pi_allocated, CTLFLAG_RD, 186161678Sdavidxu &umtx_pi_allocated, 0, "Allocated umtx_pi"); 187161678Sdavidxu 188161678Sdavidxustatic void umtxq_sysinit(void *); 189161678Sdavidxustatic void umtxq_hash(struct umtx_key *key); 190161678Sdavidxustatic struct umtxq_chain *umtxq_getchain(struct umtx_key *key); 191139013Sdavidxustatic void umtxq_lock(struct umtx_key *key); 192139013Sdavidxustatic void umtxq_unlock(struct umtx_key *key); 193139257Sdavidxustatic void umtxq_busy(struct umtx_key *key); 194139257Sdavidxustatic void umtxq_unbusy(struct umtx_key *key); 195139013Sdavidxustatic void umtxq_insert(struct umtx_q *uq); 196139013Sdavidxustatic void umtxq_remove(struct umtx_q *uq); 197161678Sdavidxustatic int umtxq_sleep(struct umtx_q *uq, const char *wmesg, int timo); 198139257Sdavidxustatic int umtxq_count(struct umtx_key *key); 199139257Sdavidxustatic int umtxq_signal(struct umtx_key *key, int nr_wakeup); 200139013Sdavidxustatic int umtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2); 201161678Sdavidxustatic int umtx_key_get(void *addr, int type, int share, 202139013Sdavidxu struct umtx_key *key); 203139013Sdavidxustatic void umtx_key_release(struct umtx_key *key); 204161678Sdavidxustatic struct umtx_pi *umtx_pi_alloc(void); 205161678Sdavidxustatic void umtx_pi_free(struct umtx_pi *pi); 206161678Sdavidxustatic int do_unlock_pp(struct thread *td, struct umutex *m, uint32_t flags); 207161678Sdavidxustatic void umtx_thread_cleanup(struct thread *td); 208161678Sdavidxustatic void umtx_exec_hook(void *arg __unused, struct proc *p __unused, 209161678Sdavidxu struct image_params *imgp __unused); 210161678SdavidxuSYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_sysinit, NULL); 211115310Sjeff 212161678Sdavidxustatic void 213161678Sdavidxuumtxq_sysinit(void *arg __unused) 214161678Sdavidxu{ 215161678Sdavidxu int i; 216138224Sdavidxu 217161678Sdavidxu umtx_pi_zone = uma_zcreate("umtx pi", sizeof(struct umtx_pi), 218161678Sdavidxu NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 219161678Sdavidxu for (i = 0; i < UMTX_CHAINS; ++i) { 220161678Sdavidxu mtx_init(&umtxq_chains[i].uc_lock, "umtxql", NULL, 221161678Sdavidxu MTX_DEF | MTX_DUPOK); 222161678Sdavidxu TAILQ_INIT(&umtxq_chains[i].uc_queue); 223161678Sdavidxu TAILQ_INIT(&umtxq_chains[i].uc_pi_list); 224161678Sdavidxu umtxq_chains[i].uc_busy = 0; 225161678Sdavidxu umtxq_chains[i].uc_waiters = 0; 226161678Sdavidxu } 227161678Sdavidxu EVENTHANDLER_REGISTER(process_exec, umtx_exec_hook, NULL, 228161678Sdavidxu EVENTHANDLER_PRI_ANY); 229161678Sdavidxu} 230161678Sdavidxu 231143149Sdavidxustruct umtx_q * 232143149Sdavidxuumtxq_alloc(void) 233143149Sdavidxu{ 234161678Sdavidxu struct umtx_q *uq; 235161678Sdavidxu 236161678Sdavidxu uq = malloc(sizeof(struct umtx_q), M_UMTX, M_WAITOK | M_ZERO); 237161678Sdavidxu TAILQ_INIT(&uq->uq_pi_contested); 238161678Sdavidxu uq->uq_inherited_pri = PRI_MAX; 239161678Sdavidxu return (uq); 240143149Sdavidxu} 241143149Sdavidxu 242143149Sdavidxuvoid 243143149Sdavidxuumtxq_free(struct umtx_q *uq) 244143149Sdavidxu{ 245143149Sdavidxu free(uq, M_UMTX); 246143149Sdavidxu} 247143149Sdavidxu 248161678Sdavidxustatic inline void 249139013Sdavidxuumtxq_hash(struct umtx_key *key) 250138224Sdavidxu{ 251161678Sdavidxu unsigned n = (uintptr_t)key->info.both.a + key->info.both.b; 252161678Sdavidxu key->hash = ((n * GOLDEN_RATIO_PRIME) >> UMTX_SHIFTS) % UMTX_CHAINS; 253138224Sdavidxu} 254138224Sdavidxu 255139013Sdavidxustatic inline int 256139013Sdavidxuumtx_key_match(const struct umtx_key *k1, const struct umtx_key *k2) 257139013Sdavidxu{ 258139013Sdavidxu return (k1->type == k2->type && 259161678Sdavidxu k1->info.both.a == k2->info.both.a && 260161678Sdavidxu k1->info.both.b == k2->info.both.b); 261139013Sdavidxu} 262139013Sdavidxu 263161678Sdavidxustatic inline struct umtxq_chain * 264161678Sdavidxuumtxq_getchain(struct umtx_key *key) 265139013Sdavidxu{ 266161678Sdavidxu return (&umtxq_chains[key->hash]); 267139013Sdavidxu} 268139013Sdavidxu 269161678Sdavidxu/* 270161678Sdavidxu * Set chain to busy state when following operation 271161678Sdavidxu * may be blocked (kernel mutex can not be used). 272161678Sdavidxu */ 273138224Sdavidxustatic inline void 274139257Sdavidxuumtxq_busy(struct umtx_key *key) 275139257Sdavidxu{ 276161678Sdavidxu struct umtxq_chain *uc; 277139257Sdavidxu 278161678Sdavidxu uc = umtxq_getchain(key); 279161678Sdavidxu mtx_assert(&uc->uc_lock, MA_OWNED); 280161678Sdavidxu while (uc->uc_busy != 0) { 281161678Sdavidxu uc->uc_waiters++; 282161678Sdavidxu msleep(uc, &uc->uc_lock, 0, "umtxqb", 0); 283161678Sdavidxu uc->uc_waiters--; 284139257Sdavidxu } 285161678Sdavidxu uc->uc_busy = 1; 286139257Sdavidxu} 287139257Sdavidxu 288161678Sdavidxu/* 289161678Sdavidxu * Unbusy a chain. 290161678Sdavidxu */ 291139257Sdavidxustatic inline void 292139257Sdavidxuumtxq_unbusy(struct umtx_key *key) 293139257Sdavidxu{ 294161678Sdavidxu struct umtxq_chain *uc; 295139257Sdavidxu 296161678Sdavidxu uc = umtxq_getchain(key); 297161678Sdavidxu mtx_assert(&uc->uc_lock, MA_OWNED); 298161678Sdavidxu KASSERT(uc->uc_busy != 0, ("not busy")); 299161678Sdavidxu uc->uc_busy = 0; 300161678Sdavidxu if (uc->uc_waiters) 301161678Sdavidxu wakeup_one(uc); 302139257Sdavidxu} 303139257Sdavidxu 304161678Sdavidxu/* 305161678Sdavidxu * Lock a chain. 306161678Sdavidxu */ 307139257Sdavidxustatic inline void 308139013Sdavidxuumtxq_lock(struct umtx_key *key) 309138224Sdavidxu{ 310161678Sdavidxu struct umtxq_chain *uc; 311161678Sdavidxu 312161678Sdavidxu uc = umtxq_getchain(key); 313161678Sdavidxu mtx_lock(&uc->uc_lock); 314138224Sdavidxu} 315138224Sdavidxu 316161678Sdavidxu/* 317161678Sdavidxu * Unlock a chain. 318161678Sdavidxu */ 319138225Sdavidxustatic inline void 320139013Sdavidxuumtxq_unlock(struct umtx_key *key) 321138224Sdavidxu{ 322161678Sdavidxu struct umtxq_chain *uc; 323161678Sdavidxu 324161678Sdavidxu uc = umtxq_getchain(key); 325161678Sdavidxu mtx_unlock(&uc->uc_lock); 326138224Sdavidxu} 327138224Sdavidxu 328139013Sdavidxu/* 329139013Sdavidxu * Insert a thread onto the umtx queue. 330139013Sdavidxu */ 331139013Sdavidxustatic inline void 332139013Sdavidxuumtxq_insert(struct umtx_q *uq) 333115765Sjeff{ 334161678Sdavidxu struct umtxq_chain *uc; 335139013Sdavidxu 336161678Sdavidxu uc = umtxq_getchain(&uq->uq_key); 337161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 338161678Sdavidxu TAILQ_INSERT_TAIL(&uc->uc_queue, uq, uq_link); 339158718Sdavidxu uq->uq_flags |= UQF_UMTXQ; 340139013Sdavidxu} 341139013Sdavidxu 342139013Sdavidxu/* 343139013Sdavidxu * Remove thread from the umtx queue. 344139013Sdavidxu */ 345139013Sdavidxustatic inline void 346139013Sdavidxuumtxq_remove(struct umtx_q *uq) 347139013Sdavidxu{ 348161678Sdavidxu struct umtxq_chain *uc; 349161678Sdavidxu 350161678Sdavidxu uc = umtxq_getchain(&uq->uq_key); 351161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 352158718Sdavidxu if (uq->uq_flags & UQF_UMTXQ) { 353161678Sdavidxu TAILQ_REMOVE(&uc->uc_queue, uq, uq_link); 354158718Sdavidxu uq->uq_flags &= ~UQF_UMTXQ; 355139013Sdavidxu } 356139013Sdavidxu} 357139013Sdavidxu 358161678Sdavidxu/* 359161678Sdavidxu * Check if there are multiple waiters 360161678Sdavidxu */ 361139013Sdavidxustatic int 362139013Sdavidxuumtxq_count(struct umtx_key *key) 363139013Sdavidxu{ 364161678Sdavidxu struct umtxq_chain *uc; 365115765Sjeff struct umtx_q *uq; 366161678Sdavidxu int count = 0; 367115765Sjeff 368161678Sdavidxu uc = umtxq_getchain(key); 369161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 370161678Sdavidxu TAILQ_FOREACH(uq, &uc->uc_queue, uq_link) { 371139013Sdavidxu if (umtx_key_match(&uq->uq_key, key)) { 372139013Sdavidxu if (++count > 1) 373139013Sdavidxu break; 374139013Sdavidxu } 375115765Sjeff } 376139013Sdavidxu return (count); 377115765Sjeff} 378115765Sjeff 379161678Sdavidxu/* 380161678Sdavidxu * Check if there are multiple PI waiters and returns first 381161678Sdavidxu * waiter. 382161678Sdavidxu */ 383139257Sdavidxustatic int 384161678Sdavidxuumtxq_count_pi(struct umtx_key *key, struct umtx_q **first) 385161678Sdavidxu{ 386161678Sdavidxu struct umtxq_chain *uc; 387161678Sdavidxu struct umtx_q *uq; 388161678Sdavidxu int count = 0; 389161678Sdavidxu 390161678Sdavidxu *first = NULL; 391161678Sdavidxu uc = umtxq_getchain(key); 392161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 393161678Sdavidxu TAILQ_FOREACH(uq, &uc->uc_queue, uq_link) { 394161678Sdavidxu if (umtx_key_match(&uq->uq_key, key)) { 395161678Sdavidxu if (++count > 1) 396161678Sdavidxu break; 397161678Sdavidxu *first = uq; 398161678Sdavidxu } 399161678Sdavidxu } 400161678Sdavidxu return (count); 401161678Sdavidxu} 402161678Sdavidxu 403161678Sdavidxu/* 404161678Sdavidxu * Wake up threads waiting on an userland object. 405161678Sdavidxu */ 406161678Sdavidxustatic int 407139257Sdavidxuumtxq_signal(struct umtx_key *key, int n_wake) 408115765Sjeff{ 409161678Sdavidxu struct umtxq_chain *uc; 410139257Sdavidxu struct umtx_q *uq, *next; 411161678Sdavidxu int ret; 412115765Sjeff 413139257Sdavidxu ret = 0; 414161678Sdavidxu uc = umtxq_getchain(key); 415161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 416161678Sdavidxu TAILQ_FOREACH_SAFE(uq, &uc->uc_queue, uq_link, next) { 417139013Sdavidxu if (umtx_key_match(&uq->uq_key, key)) { 418139013Sdavidxu umtxq_remove(uq); 419161678Sdavidxu wakeup(uq); 420139257Sdavidxu if (++ret >= n_wake) 421139257Sdavidxu break; 422139013Sdavidxu } 423139013Sdavidxu } 424139257Sdavidxu return (ret); 425138224Sdavidxu} 426138224Sdavidxu 427161678Sdavidxu/* 428161678Sdavidxu * Wake up specified thread. 429161678Sdavidxu */ 430161678Sdavidxustatic inline void 431161678Sdavidxuumtxq_signal_thread(struct umtx_q *uq) 432161678Sdavidxu{ 433161678Sdavidxu struct umtxq_chain *uc; 434161678Sdavidxu 435161678Sdavidxu uc = umtxq_getchain(&uq->uq_key); 436161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 437161678Sdavidxu umtxq_remove(uq); 438161678Sdavidxu wakeup(uq); 439161678Sdavidxu} 440161678Sdavidxu 441161678Sdavidxu/* 442161678Sdavidxu * Put thread into sleep state, before sleeping, check if 443161678Sdavidxu * thread was removed from umtx queue. 444161678Sdavidxu */ 445138224Sdavidxustatic inline int 446161678Sdavidxuumtxq_sleep(struct umtx_q *uq, const char *wmesg, int timo) 447138224Sdavidxu{ 448161678Sdavidxu struct umtxq_chain *uc; 449161678Sdavidxu int error; 450161678Sdavidxu 451161678Sdavidxu uc = umtxq_getchain(&uq->uq_key); 452161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 453161678Sdavidxu if (!(uq->uq_flags & UQF_UMTXQ)) 454161678Sdavidxu return (0); 455161678Sdavidxu error = msleep(uq, &uc->uc_lock, PCATCH, wmesg, timo); 456139751Sdavidxu if (error == EWOULDBLOCK) 457139751Sdavidxu error = ETIMEDOUT; 458139751Sdavidxu return (error); 459138224Sdavidxu} 460138224Sdavidxu 461161678Sdavidxu/* 462161678Sdavidxu * Convert userspace address into unique logical address. 463161678Sdavidxu */ 464139013Sdavidxustatic int 465161678Sdavidxuumtx_key_get(void *addr, int type, int share, struct umtx_key *key) 466139013Sdavidxu{ 467161678Sdavidxu struct thread *td = curthread; 468139013Sdavidxu vm_map_t map; 469139013Sdavidxu vm_map_entry_t entry; 470139013Sdavidxu vm_pindex_t pindex; 471139013Sdavidxu vm_prot_t prot; 472139013Sdavidxu boolean_t wired; 473139013Sdavidxu 474161678Sdavidxu key->type = type; 475161678Sdavidxu if (share == THREAD_SHARE) { 476161678Sdavidxu key->shared = 0; 477161678Sdavidxu key->info.private.vs = td->td_proc->p_vmspace; 478161678Sdavidxu key->info.private.addr = (uintptr_t)addr; 479161678Sdavidxu } else if (share == PROCESS_SHARE || share == AUTO_SHARE) { 480161678Sdavidxu map = &td->td_proc->p_vmspace->vm_map; 481161678Sdavidxu if (vm_map_lookup(&map, (vm_offset_t)addr, VM_PROT_WRITE, 482161678Sdavidxu &entry, &key->info.shared.object, &pindex, &prot, 483161678Sdavidxu &wired) != KERN_SUCCESS) { 484161678Sdavidxu return EFAULT; 485161678Sdavidxu } 486161678Sdavidxu 487161678Sdavidxu if ((share == PROCESS_SHARE) || 488161678Sdavidxu (share == AUTO_SHARE && 489161678Sdavidxu VM_INHERIT_SHARE == entry->inheritance)) { 490161678Sdavidxu key->shared = 1; 491161678Sdavidxu key->info.shared.offset = entry->offset + entry->start - 492161678Sdavidxu (vm_offset_t)addr; 493161678Sdavidxu vm_object_reference(key->info.shared.object); 494161678Sdavidxu } else { 495161678Sdavidxu key->shared = 0; 496161678Sdavidxu key->info.private.vs = td->td_proc->p_vmspace; 497161678Sdavidxu key->info.private.addr = (uintptr_t)addr; 498161678Sdavidxu } 499161678Sdavidxu vm_map_lookup_done(map, entry); 500139013Sdavidxu } 501139013Sdavidxu 502161678Sdavidxu umtxq_hash(key); 503139013Sdavidxu return (0); 504139013Sdavidxu} 505139013Sdavidxu 506161678Sdavidxu/* 507161678Sdavidxu * Release key. 508161678Sdavidxu */ 509139013Sdavidxustatic inline void 510139013Sdavidxuumtx_key_release(struct umtx_key *key) 511139013Sdavidxu{ 512161678Sdavidxu if (key->shared) 513139013Sdavidxu vm_object_deallocate(key->info.shared.object); 514139013Sdavidxu} 515139013Sdavidxu 516161678Sdavidxu/* 517161678Sdavidxu * Lock a umtx object. 518161678Sdavidxu */ 519139013Sdavidxustatic int 520161678Sdavidxu_do_lock(struct thread *td, struct umtx *umtx, uintptr_t id, int timo) 521112904Sjeff{ 522143149Sdavidxu struct umtx_q *uq; 523112904Sjeff intptr_t owner; 524112967Sjake intptr_t old; 525138224Sdavidxu int error = 0; 526112904Sjeff 527143149Sdavidxu uq = td->td_umtxq; 528161678Sdavidxu 529112904Sjeff /* 530161678Sdavidxu * Care must be exercised when dealing with umtx structure. It 531112904Sjeff * can fault on any access. 532112904Sjeff */ 533112904Sjeff for (;;) { 534112904Sjeff /* 535112904Sjeff * Try the uncontested case. This should be done in userland. 536112904Sjeff */ 537161678Sdavidxu owner = casuptr((intptr_t *)&umtx->u_owner, UMTX_UNOWNED, id); 538112904Sjeff 539138224Sdavidxu /* The acquire succeeded. */ 540138224Sdavidxu if (owner == UMTX_UNOWNED) 541138224Sdavidxu return (0); 542138224Sdavidxu 543115765Sjeff /* The address was invalid. */ 544115765Sjeff if (owner == -1) 545115765Sjeff return (EFAULT); 546115765Sjeff 547115765Sjeff /* If no one owns it but it is contested try to acquire it. */ 548115765Sjeff if (owner == UMTX_CONTESTED) { 549115765Sjeff owner = casuptr((intptr_t *)&umtx->u_owner, 550139013Sdavidxu UMTX_CONTESTED, id | UMTX_CONTESTED); 551115765Sjeff 552138224Sdavidxu if (owner == UMTX_CONTESTED) 553138224Sdavidxu return (0); 554138224Sdavidxu 555115765Sjeff /* The address was invalid. */ 556115765Sjeff if (owner == -1) 557115765Sjeff return (EFAULT); 558115765Sjeff 559115765Sjeff /* If this failed the lock has changed, restart. */ 560115765Sjeff continue; 561112904Sjeff } 562112904Sjeff 563138224Sdavidxu /* 564138224Sdavidxu * If we caught a signal, we have retried and now 565138224Sdavidxu * exit immediately. 566138224Sdavidxu */ 567161678Sdavidxu if (error != 0) 568138224Sdavidxu return (error); 569112904Sjeff 570161678Sdavidxu if ((error = umtx_key_get(umtx, TYPE_SIMPLE_LOCK, 571161678Sdavidxu AUTO_SHARE, &uq->uq_key)) != 0) 572161678Sdavidxu return (error); 573161678Sdavidxu 574161678Sdavidxu umtxq_lock(&uq->uq_key); 575161678Sdavidxu umtxq_busy(&uq->uq_key); 576161678Sdavidxu umtxq_insert(uq); 577161678Sdavidxu umtxq_unbusy(&uq->uq_key); 578161678Sdavidxu umtxq_unlock(&uq->uq_key); 579161678Sdavidxu 580112904Sjeff /* 581112904Sjeff * Set the contested bit so that a release in user space 582112904Sjeff * knows to use the system call for unlock. If this fails 583112904Sjeff * either some one else has acquired the lock or it has been 584112904Sjeff * released. 585112904Sjeff */ 586112967Sjake old = casuptr((intptr_t *)&umtx->u_owner, owner, 587112967Sjake owner | UMTX_CONTESTED); 588112904Sjeff 589112904Sjeff /* The address was invalid. */ 590112967Sjake if (old == -1) { 591143149Sdavidxu umtxq_lock(&uq->uq_key); 592143149Sdavidxu umtxq_remove(uq); 593143149Sdavidxu umtxq_unlock(&uq->uq_key); 594143149Sdavidxu umtx_key_release(&uq->uq_key); 595115765Sjeff return (EFAULT); 596112904Sjeff } 597112904Sjeff 598112904Sjeff /* 599115765Sjeff * We set the contested bit, sleep. Otherwise the lock changed 600117685Smtm * and we need to retry or we lost a race to the thread 601117685Smtm * unlocking the umtx. 602112904Sjeff */ 603143149Sdavidxu umtxq_lock(&uq->uq_key); 604161678Sdavidxu if (old == owner) 605161678Sdavidxu error = umtxq_sleep(uq, "umtx", timo); 606143149Sdavidxu umtxq_remove(uq); 607143149Sdavidxu umtxq_unlock(&uq->uq_key); 608143149Sdavidxu umtx_key_release(&uq->uq_key); 609112904Sjeff } 610117743Smtm 611117743Smtm return (0); 612112904Sjeff} 613112904Sjeff 614161678Sdavidxu/* 615161678Sdavidxu * Lock a umtx object. 616161678Sdavidxu */ 617139013Sdavidxustatic int 618161678Sdavidxudo_lock(struct thread *td, struct umtx *umtx, uintptr_t id, 619140245Sdavidxu struct timespec *timeout) 620112904Sjeff{ 621140245Sdavidxu struct timespec ts, ts2, ts3; 622139013Sdavidxu struct timeval tv; 623140245Sdavidxu int error; 624139013Sdavidxu 625140245Sdavidxu if (timeout == NULL) { 626139013Sdavidxu error = _do_lock(td, umtx, id, 0); 627139013Sdavidxu } else { 628140245Sdavidxu getnanouptime(&ts); 629140245Sdavidxu timespecadd(&ts, timeout); 630140245Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, timeout); 631139013Sdavidxu for (;;) { 632140245Sdavidxu error = _do_lock(td, umtx, id, tvtohz(&tv)); 633140245Sdavidxu if (error != ETIMEDOUT) 634140245Sdavidxu break; 635140245Sdavidxu getnanouptime(&ts2); 636140245Sdavidxu if (timespeccmp(&ts2, &ts, >=)) { 637139751Sdavidxu error = ETIMEDOUT; 638139013Sdavidxu break; 639139013Sdavidxu } 640140245Sdavidxu ts3 = ts; 641140245Sdavidxu timespecsub(&ts3, &ts2); 642140245Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, &ts3); 643139013Sdavidxu } 644139013Sdavidxu } 645161684Sdavidxu /* Mutex locking is be restarted if it is interrupted. */ 646161678Sdavidxu if (error == EINTR) 647161678Sdavidxu error = ERESTART; 648139013Sdavidxu return (error); 649139013Sdavidxu} 650139013Sdavidxu 651161678Sdavidxu/* 652161678Sdavidxu * Unlock a umtx object. 653161678Sdavidxu */ 654139013Sdavidxustatic int 655161678Sdavidxudo_unlock(struct thread *td, struct umtx *umtx, uintptr_t id) 656139013Sdavidxu{ 657139013Sdavidxu struct umtx_key key; 658112904Sjeff intptr_t owner; 659112967Sjake intptr_t old; 660139257Sdavidxu int error; 661139257Sdavidxu int count; 662112904Sjeff 663112904Sjeff /* 664112904Sjeff * Make sure we own this mtx. 665112904Sjeff * 666112904Sjeff * XXX Need a {fu,su}ptr this is not correct on arch where 667112904Sjeff * sizeof(intptr_t) != sizeof(long). 668112904Sjeff */ 669161678Sdavidxu owner = fuword(&umtx->u_owner); 670161678Sdavidxu if (owner == -1) 671115765Sjeff return (EFAULT); 672115765Sjeff 673139013Sdavidxu if ((owner & ~UMTX_CONTESTED) != id) 674115765Sjeff return (EPERM); 675112904Sjeff 676161678Sdavidxu /* This should be done in userland */ 677161678Sdavidxu if ((owner & UMTX_CONTESTED) == 0) { 678161678Sdavidxu old = casuptr((intptr_t *)&umtx->u_owner, owner, 679161678Sdavidxu UMTX_UNOWNED); 680161678Sdavidxu if (old == -1) 681161678Sdavidxu return (EFAULT); 682161678Sdavidxu if (old == owner) 683161678Sdavidxu return (0); 684161855Sdavidxu owner = old; 685161678Sdavidxu } 686161678Sdavidxu 687117685Smtm /* We should only ever be in here for contested locks */ 688161678Sdavidxu if ((error = umtx_key_get(umtx, TYPE_SIMPLE_LOCK, AUTO_SHARE, 689161678Sdavidxu &key)) != 0) 690139257Sdavidxu return (error); 691139257Sdavidxu 692139257Sdavidxu umtxq_lock(&key); 693139257Sdavidxu umtxq_busy(&key); 694139257Sdavidxu count = umtxq_count(&key); 695139257Sdavidxu umtxq_unlock(&key); 696139257Sdavidxu 697117743Smtm /* 698117743Smtm * When unlocking the umtx, it must be marked as unowned if 699117743Smtm * there is zero or one thread only waiting for it. 700117743Smtm * Otherwise, it must be marked as contested. 701117743Smtm */ 702139257Sdavidxu old = casuptr((intptr_t *)&umtx->u_owner, owner, 703139257Sdavidxu count <= 1 ? UMTX_UNOWNED : UMTX_CONTESTED); 704139257Sdavidxu umtxq_lock(&key); 705161678Sdavidxu umtxq_signal(&key,1); 706139257Sdavidxu umtxq_unbusy(&key); 707139257Sdavidxu umtxq_unlock(&key); 708139257Sdavidxu umtx_key_release(&key); 709115765Sjeff if (old == -1) 710115765Sjeff return (EFAULT); 711138224Sdavidxu if (old != owner) 712138224Sdavidxu return (EINVAL); 713115765Sjeff return (0); 714112904Sjeff} 715139013Sdavidxu 716161678Sdavidxu/* 717161678Sdavidxu * Fetch and compare value, sleep on the address if value is not changed. 718161678Sdavidxu */ 719139013Sdavidxustatic int 720161678Sdavidxudo_wait(struct thread *td, struct umtx *umtx, uintptr_t id, struct timespec *timeout) 721139013Sdavidxu{ 722143149Sdavidxu struct umtx_q *uq; 723140245Sdavidxu struct timespec ts, ts2, ts3; 724139013Sdavidxu struct timeval tv; 725161678Sdavidxu uintptr_t tmp; 726140245Sdavidxu int error = 0; 727139013Sdavidxu 728143149Sdavidxu uq = td->td_umtxq; 729161678Sdavidxu if ((error = umtx_key_get(umtx, TYPE_SIMPLE_WAIT, AUTO_SHARE, 730161678Sdavidxu &uq->uq_key)) != 0) 731139013Sdavidxu return (error); 732161678Sdavidxu 733161678Sdavidxu umtxq_lock(&uq->uq_key); 734161678Sdavidxu umtxq_insert(uq); 735161678Sdavidxu umtxq_unlock(&uq->uq_key); 736139427Sdavidxu tmp = fuword(&umtx->u_owner); 737139427Sdavidxu if (tmp != id) { 738143149Sdavidxu umtxq_lock(&uq->uq_key); 739143149Sdavidxu umtxq_remove(uq); 740143149Sdavidxu umtxq_unlock(&uq->uq_key); 741140245Sdavidxu } else if (timeout == NULL) { 742143149Sdavidxu umtxq_lock(&uq->uq_key); 743161678Sdavidxu error = umtxq_sleep(uq, "ucond", 0); 744161678Sdavidxu umtxq_remove(uq); 745143149Sdavidxu umtxq_unlock(&uq->uq_key); 746139013Sdavidxu } else { 747140245Sdavidxu getnanouptime(&ts); 748140245Sdavidxu timespecadd(&ts, timeout); 749140245Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, timeout); 750161678Sdavidxu umtxq_lock(&uq->uq_key); 751139013Sdavidxu for (;;) { 752161678Sdavidxu error = umtxq_sleep(uq, "ucond", tvtohz(&tv)); 753161678Sdavidxu if (!(uq->uq_flags & UQF_UMTXQ)) 754161678Sdavidxu break; 755140245Sdavidxu if (error != ETIMEDOUT) 756140245Sdavidxu break; 757161678Sdavidxu umtxq_unlock(&uq->uq_key); 758140245Sdavidxu getnanouptime(&ts2); 759140245Sdavidxu if (timespeccmp(&ts2, &ts, >=)) { 760139751Sdavidxu error = ETIMEDOUT; 761161678Sdavidxu umtxq_lock(&uq->uq_key); 762139013Sdavidxu break; 763139013Sdavidxu } 764140245Sdavidxu ts3 = ts; 765140245Sdavidxu timespecsub(&ts3, &ts2); 766140245Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, &ts3); 767161678Sdavidxu umtxq_lock(&uq->uq_key); 768139013Sdavidxu } 769143149Sdavidxu umtxq_remove(uq); 770143149Sdavidxu umtxq_unlock(&uq->uq_key); 771139013Sdavidxu } 772143149Sdavidxu umtx_key_release(&uq->uq_key); 773161684Sdavidxu /* Mutex locking is be restarted if it is interrupted. */ 774139257Sdavidxu if (error == ERESTART) 775139257Sdavidxu error = EINTR; 776139013Sdavidxu return (error); 777139013Sdavidxu} 778139013Sdavidxu 779161678Sdavidxu/* 780161678Sdavidxu * Wake up threads sleeping on the specified address. 781161678Sdavidxu */ 782151692Sdavidxuint 783151692Sdavidxukern_umtx_wake(struct thread *td, void *uaddr, int n_wake) 784139013Sdavidxu{ 785139013Sdavidxu struct umtx_key key; 786139257Sdavidxu int ret; 787139013Sdavidxu 788161678Sdavidxu if ((ret = umtx_key_get(uaddr, TYPE_SIMPLE_WAIT, AUTO_SHARE, 789161678Sdavidxu &key)) != 0) 790139257Sdavidxu return (ret); 791139258Sdavidxu umtxq_lock(&key); 792139257Sdavidxu ret = umtxq_signal(&key, n_wake); 793139258Sdavidxu umtxq_unlock(&key); 794139257Sdavidxu umtx_key_release(&key); 795139013Sdavidxu return (0); 796139013Sdavidxu} 797139013Sdavidxu 798161678Sdavidxu/* 799161678Sdavidxu * Lock PTHREAD_PRIO_NONE protocol POSIX mutex. 800161678Sdavidxu */ 801161678Sdavidxustatic int 802161678Sdavidxu_do_lock_normal(struct thread *td, struct umutex *m, uint32_t flags, int timo, 803161678Sdavidxu int try) 804161678Sdavidxu{ 805161678Sdavidxu struct umtx_q *uq; 806161678Sdavidxu uint32_t owner, old, id; 807161678Sdavidxu int error = 0; 808161678Sdavidxu 809161678Sdavidxu id = td->td_tid; 810161678Sdavidxu uq = td->td_umtxq; 811161678Sdavidxu 812161678Sdavidxu /* 813161678Sdavidxu * Care must be exercised when dealing with umtx structure. It 814161678Sdavidxu * can fault on any access. 815161678Sdavidxu */ 816161678Sdavidxu for (;;) { 817161678Sdavidxu /* 818161678Sdavidxu * Try the uncontested case. This should be done in userland. 819161678Sdavidxu */ 820161678Sdavidxu owner = casuword32(&m->m_owner, UMUTEX_UNOWNED, id); 821161678Sdavidxu 822161678Sdavidxu /* The acquire succeeded. */ 823161678Sdavidxu if (owner == UMUTEX_UNOWNED) 824161678Sdavidxu return (0); 825161678Sdavidxu 826161678Sdavidxu /* The address was invalid. */ 827161678Sdavidxu if (owner == -1) 828161678Sdavidxu return (EFAULT); 829161678Sdavidxu 830161678Sdavidxu /* If no one owns it but it is contested try to acquire it. */ 831161678Sdavidxu if (owner == UMUTEX_CONTESTED) { 832161678Sdavidxu owner = casuword32(&m->m_owner, 833161678Sdavidxu UMUTEX_CONTESTED, id | UMUTEX_CONTESTED); 834161678Sdavidxu 835161678Sdavidxu if (owner == UMUTEX_CONTESTED) 836161678Sdavidxu return (0); 837161678Sdavidxu 838161678Sdavidxu /* The address was invalid. */ 839161678Sdavidxu if (owner == -1) 840161678Sdavidxu return (EFAULT); 841161678Sdavidxu 842161678Sdavidxu /* If this failed the lock has changed, restart. */ 843161678Sdavidxu continue; 844161678Sdavidxu } 845161678Sdavidxu 846161678Sdavidxu if ((flags & UMUTEX_ERROR_CHECK) != 0 && 847161678Sdavidxu (owner & ~UMUTEX_CONTESTED) == id) 848161678Sdavidxu return (EDEADLK); 849161678Sdavidxu 850161678Sdavidxu if (try != 0) 851161678Sdavidxu return (EBUSY); 852161678Sdavidxu 853161678Sdavidxu /* 854161678Sdavidxu * If we caught a signal, we have retried and now 855161678Sdavidxu * exit immediately. 856161678Sdavidxu */ 857161678Sdavidxu if (error != 0) 858161678Sdavidxu return (error); 859161678Sdavidxu 860161678Sdavidxu if ((error = umtx_key_get(m, TYPE_NORMAL_UMUTEX, 861161678Sdavidxu GET_SHARE(flags), &uq->uq_key)) != 0) 862161678Sdavidxu return (error); 863161678Sdavidxu 864161678Sdavidxu umtxq_lock(&uq->uq_key); 865161678Sdavidxu umtxq_busy(&uq->uq_key); 866161678Sdavidxu umtxq_insert(uq); 867161678Sdavidxu umtxq_unbusy(&uq->uq_key); 868161678Sdavidxu umtxq_unlock(&uq->uq_key); 869161678Sdavidxu 870161678Sdavidxu /* 871161678Sdavidxu * Set the contested bit so that a release in user space 872161678Sdavidxu * knows to use the system call for unlock. If this fails 873161678Sdavidxu * either some one else has acquired the lock or it has been 874161678Sdavidxu * released. 875161678Sdavidxu */ 876161678Sdavidxu old = casuword32(&m->m_owner, owner, owner | UMUTEX_CONTESTED); 877161678Sdavidxu 878161678Sdavidxu /* The address was invalid. */ 879161678Sdavidxu if (old == -1) { 880161678Sdavidxu umtxq_lock(&uq->uq_key); 881161678Sdavidxu umtxq_remove(uq); 882161678Sdavidxu umtxq_unlock(&uq->uq_key); 883161678Sdavidxu umtx_key_release(&uq->uq_key); 884161678Sdavidxu return (EFAULT); 885161678Sdavidxu } 886161678Sdavidxu 887161678Sdavidxu /* 888161678Sdavidxu * We set the contested bit, sleep. Otherwise the lock changed 889161678Sdavidxu * and we need to retry or we lost a race to the thread 890161678Sdavidxu * unlocking the umtx. 891161678Sdavidxu */ 892161678Sdavidxu umtxq_lock(&uq->uq_key); 893161678Sdavidxu if (old == owner) 894161678Sdavidxu error = umtxq_sleep(uq, "umtxn", timo); 895161678Sdavidxu umtxq_remove(uq); 896161678Sdavidxu umtxq_unlock(&uq->uq_key); 897161678Sdavidxu umtx_key_release(&uq->uq_key); 898161678Sdavidxu } 899161678Sdavidxu 900161678Sdavidxu return (0); 901161678Sdavidxu} 902161678Sdavidxu 903161678Sdavidxu/* 904161678Sdavidxu * Lock PTHREAD_PRIO_NONE protocol POSIX mutex. 905161678Sdavidxu */ 906161678Sdavidxustatic int 907161678Sdavidxudo_lock_normal(struct thread *td, struct umutex *m, uint32_t flags, 908161678Sdavidxu struct timespec *timeout, int try) 909161678Sdavidxu{ 910161678Sdavidxu struct timespec ts, ts2, ts3; 911161678Sdavidxu struct timeval tv; 912161678Sdavidxu int error; 913161678Sdavidxu 914161678Sdavidxu if (timeout == NULL) { 915161678Sdavidxu error = _do_lock_normal(td, m, flags, 0, try); 916161678Sdavidxu } else { 917161678Sdavidxu getnanouptime(&ts); 918161678Sdavidxu timespecadd(&ts, timeout); 919161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, timeout); 920161678Sdavidxu for (;;) { 921161678Sdavidxu error = _do_lock_normal(td, m, flags, tvtohz(&tv), try); 922161678Sdavidxu if (error != ETIMEDOUT) 923161678Sdavidxu break; 924161678Sdavidxu getnanouptime(&ts2); 925161678Sdavidxu if (timespeccmp(&ts2, &ts, >=)) { 926161678Sdavidxu error = ETIMEDOUT; 927161678Sdavidxu break; 928161678Sdavidxu } 929161678Sdavidxu ts3 = ts; 930161678Sdavidxu timespecsub(&ts3, &ts2); 931161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, &ts3); 932161678Sdavidxu } 933161678Sdavidxu } 934161684Sdavidxu /* Mutex locking is be restarted if it is interrupted. */ 935161678Sdavidxu if (error == EINTR) 936161678Sdavidxu error = ERESTART; 937161678Sdavidxu return (error); 938161678Sdavidxu} 939161678Sdavidxu 940161678Sdavidxu/* 941161678Sdavidxu * Unlock PTHREAD_PRIO_NONE protocol POSIX mutex. 942161678Sdavidxu */ 943161678Sdavidxustatic int 944161678Sdavidxudo_unlock_normal(struct thread *td, struct umutex *m, uint32_t flags) 945161678Sdavidxu{ 946161678Sdavidxu struct umtx_key key; 947161678Sdavidxu uint32_t owner, old, id; 948161678Sdavidxu int error; 949161678Sdavidxu int count; 950161678Sdavidxu 951161678Sdavidxu id = td->td_tid; 952161678Sdavidxu /* 953161678Sdavidxu * Make sure we own this mtx. 954161678Sdavidxu */ 955161678Sdavidxu owner = fuword32(&m->m_owner); 956161678Sdavidxu if (owner == -1) 957161678Sdavidxu return (EFAULT); 958161678Sdavidxu 959161678Sdavidxu if ((owner & ~UMUTEX_CONTESTED) != id) 960161678Sdavidxu return (EPERM); 961161678Sdavidxu 962161678Sdavidxu /* This should be done in userland */ 963161678Sdavidxu if ((owner & UMUTEX_CONTESTED) == 0) { 964161678Sdavidxu old = casuword32(&m->m_owner, owner, UMUTEX_UNOWNED); 965161678Sdavidxu if (old == -1) 966161678Sdavidxu return (EFAULT); 967161678Sdavidxu if (old == owner) 968161678Sdavidxu return (0); 969161855Sdavidxu owner = old; 970161678Sdavidxu } 971161678Sdavidxu 972161678Sdavidxu /* We should only ever be in here for contested locks */ 973161678Sdavidxu if ((error = umtx_key_get(m, TYPE_NORMAL_UMUTEX, GET_SHARE(flags), 974161678Sdavidxu &key)) != 0) 975161678Sdavidxu return (error); 976161678Sdavidxu 977161678Sdavidxu umtxq_lock(&key); 978161678Sdavidxu umtxq_busy(&key); 979161678Sdavidxu count = umtxq_count(&key); 980161678Sdavidxu umtxq_unlock(&key); 981161678Sdavidxu 982161678Sdavidxu /* 983161678Sdavidxu * When unlocking the umtx, it must be marked as unowned if 984161678Sdavidxu * there is zero or one thread only waiting for it. 985161678Sdavidxu * Otherwise, it must be marked as contested. 986161678Sdavidxu */ 987161678Sdavidxu old = casuword32(&m->m_owner, owner, 988161678Sdavidxu count <= 1 ? UMUTEX_UNOWNED : UMUTEX_CONTESTED); 989161678Sdavidxu umtxq_lock(&key); 990161678Sdavidxu umtxq_signal(&key,1); 991161678Sdavidxu umtxq_unbusy(&key); 992161678Sdavidxu umtxq_unlock(&key); 993161678Sdavidxu umtx_key_release(&key); 994161678Sdavidxu if (old == -1) 995161678Sdavidxu return (EFAULT); 996161678Sdavidxu if (old != owner) 997161678Sdavidxu return (EINVAL); 998161678Sdavidxu return (0); 999161678Sdavidxu} 1000161678Sdavidxu 1001161678Sdavidxustatic inline struct umtx_pi * 1002161678Sdavidxuumtx_pi_alloc(void) 1003161678Sdavidxu{ 1004161678Sdavidxu struct umtx_pi *pi; 1005161678Sdavidxu 1006161678Sdavidxu pi = uma_zalloc(umtx_pi_zone, M_ZERO | M_WAITOK); 1007161678Sdavidxu TAILQ_INIT(&pi->pi_blocked); 1008161678Sdavidxu atomic_add_int(&umtx_pi_allocated, 1); 1009161678Sdavidxu return (pi); 1010161678Sdavidxu} 1011161678Sdavidxu 1012161678Sdavidxustatic inline void 1013161678Sdavidxuumtx_pi_free(struct umtx_pi *pi) 1014161678Sdavidxu{ 1015161678Sdavidxu uma_zfree(umtx_pi_zone, pi); 1016161678Sdavidxu atomic_add_int(&umtx_pi_allocated, -1); 1017161678Sdavidxu} 1018161678Sdavidxu 1019161678Sdavidxu/* 1020161678Sdavidxu * Adjust the thread's position on a pi_state after its priority has been 1021161678Sdavidxu * changed. 1022161678Sdavidxu */ 1023161678Sdavidxustatic int 1024161678Sdavidxuumtx_pi_adjust_thread(struct umtx_pi *pi, struct thread *td) 1025161678Sdavidxu{ 1026161678Sdavidxu struct umtx_q *uq, *uq1, *uq2; 1027161678Sdavidxu struct thread *td1; 1028161678Sdavidxu 1029161678Sdavidxu mtx_assert(&sched_lock, MA_OWNED); 1030161678Sdavidxu if (pi == NULL) 1031161678Sdavidxu return (0); 1032161678Sdavidxu 1033161678Sdavidxu uq = td->td_umtxq; 1034161678Sdavidxu 1035161678Sdavidxu /* 1036161678Sdavidxu * Check if the thread needs to be moved on the blocked chain. 1037161678Sdavidxu * It needs to be moved if either its priority is lower than 1038161678Sdavidxu * the previous thread or higher than the next thread. 1039161678Sdavidxu */ 1040161678Sdavidxu uq1 = TAILQ_PREV(uq, umtxq_head, uq_lockq); 1041161678Sdavidxu uq2 = TAILQ_NEXT(uq, uq_lockq); 1042161678Sdavidxu if ((uq1 != NULL && UPRI(td) < UPRI(uq1->uq_thread)) || 1043161678Sdavidxu (uq2 != NULL && UPRI(td) > UPRI(uq2->uq_thread))) { 1044161678Sdavidxu /* 1045161678Sdavidxu * Remove thread from blocked chain and determine where 1046161678Sdavidxu * it should be moved to. 1047161678Sdavidxu */ 1048161678Sdavidxu TAILQ_REMOVE(&pi->pi_blocked, uq, uq_lockq); 1049161678Sdavidxu TAILQ_FOREACH(uq1, &pi->pi_blocked, uq_lockq) { 1050161678Sdavidxu td1 = uq1->uq_thread; 1051161678Sdavidxu MPASS(td1->td_proc->p_magic == P_MAGIC); 1052161678Sdavidxu if (UPRI(td1) > UPRI(td)) 1053161678Sdavidxu break; 1054161678Sdavidxu } 1055161678Sdavidxu 1056161678Sdavidxu if (uq1 == NULL) 1057161678Sdavidxu TAILQ_INSERT_TAIL(&pi->pi_blocked, uq, uq_lockq); 1058161678Sdavidxu else 1059161678Sdavidxu TAILQ_INSERT_BEFORE(uq1, uq, uq_lockq); 1060161678Sdavidxu } 1061161678Sdavidxu return (1); 1062161678Sdavidxu} 1063161678Sdavidxu 1064161678Sdavidxu/* 1065161678Sdavidxu * Propagate priority when a thread is blocked on POSIX 1066161678Sdavidxu * PI mutex. 1067161678Sdavidxu */ 1068161678Sdavidxustatic void 1069161678Sdavidxuumtx_propagate_priority(struct thread *td) 1070161678Sdavidxu{ 1071161678Sdavidxu struct umtx_q *uq; 1072161678Sdavidxu struct umtx_pi *pi; 1073161678Sdavidxu int pri; 1074161678Sdavidxu 1075161678Sdavidxu mtx_assert(&sched_lock, MA_OWNED); 1076161678Sdavidxu pri = UPRI(td); 1077161678Sdavidxu uq = td->td_umtxq; 1078161678Sdavidxu pi = uq->uq_pi_blocked; 1079161678Sdavidxu if (pi == NULL) 1080161678Sdavidxu return; 1081161678Sdavidxu 1082161678Sdavidxu for (;;) { 1083161678Sdavidxu td = pi->pi_owner; 1084161678Sdavidxu if (td == NULL) 1085161678Sdavidxu return; 1086161678Sdavidxu 1087161678Sdavidxu MPASS(td->td_proc != NULL); 1088161678Sdavidxu MPASS(td->td_proc->p_magic == P_MAGIC); 1089161678Sdavidxu 1090161678Sdavidxu if (UPRI(td) <= pri) 1091161678Sdavidxu return; 1092161678Sdavidxu 1093161678Sdavidxu sched_lend_user_prio(td, pri); 1094161678Sdavidxu 1095161678Sdavidxu /* 1096161678Sdavidxu * Pick up the lock that td is blocked on. 1097161678Sdavidxu */ 1098161678Sdavidxu uq = td->td_umtxq; 1099161678Sdavidxu pi = uq->uq_pi_blocked; 1100161678Sdavidxu /* Resort td on the list if needed. */ 1101161678Sdavidxu if (!umtx_pi_adjust_thread(pi, td)) 1102161678Sdavidxu break; 1103161678Sdavidxu } 1104161678Sdavidxu} 1105161678Sdavidxu 1106161678Sdavidxu/* 1107161678Sdavidxu * Unpropagate priority for a PI mutex when a thread blocked on 1108161678Sdavidxu * it is interrupted by signal or resumed by others. 1109161678Sdavidxu */ 1110161678Sdavidxustatic void 1111161678Sdavidxuumtx_unpropagate_priority(struct umtx_pi *pi) 1112161678Sdavidxu{ 1113161678Sdavidxu struct umtx_q *uq, *uq_owner; 1114161678Sdavidxu struct umtx_pi *pi2; 1115161678Sdavidxu int pri; 1116161678Sdavidxu 1117161678Sdavidxu mtx_assert(&sched_lock, MA_OWNED); 1118161678Sdavidxu 1119161678Sdavidxu while (pi != NULL && pi->pi_owner != NULL) { 1120161678Sdavidxu pri = PRI_MAX; 1121161678Sdavidxu uq_owner = pi->pi_owner->td_umtxq; 1122161678Sdavidxu 1123161678Sdavidxu TAILQ_FOREACH(pi2, &uq_owner->uq_pi_contested, pi_link) { 1124161678Sdavidxu uq = TAILQ_FIRST(&pi2->pi_blocked); 1125161678Sdavidxu if (uq != NULL) { 1126161678Sdavidxu if (pri > UPRI(uq->uq_thread)) 1127161678Sdavidxu pri = UPRI(uq->uq_thread); 1128161678Sdavidxu } 1129161678Sdavidxu } 1130161678Sdavidxu 1131161678Sdavidxu if (pri > uq_owner->uq_inherited_pri) 1132161678Sdavidxu pri = uq_owner->uq_inherited_pri; 1133161678Sdavidxu sched_unlend_user_prio(pi->pi_owner, pri); 1134161678Sdavidxu pi = uq_owner->uq_pi_blocked; 1135161678Sdavidxu } 1136161678Sdavidxu} 1137161678Sdavidxu 1138161678Sdavidxu/* 1139161678Sdavidxu * Insert a PI mutex into owned list. 1140161678Sdavidxu */ 1141161678Sdavidxustatic void 1142161678Sdavidxuumtx_pi_setowner(struct umtx_pi *pi, struct thread *owner) 1143161678Sdavidxu{ 1144161678Sdavidxu struct umtx_q *uq_owner; 1145161678Sdavidxu 1146161678Sdavidxu uq_owner = owner->td_umtxq; 1147161678Sdavidxu mtx_assert(&sched_lock, MA_OWNED); 1148161678Sdavidxu if (pi->pi_owner != NULL) 1149161678Sdavidxu panic("pi_ower != NULL"); 1150161678Sdavidxu pi->pi_owner = owner; 1151161678Sdavidxu TAILQ_INSERT_TAIL(&uq_owner->uq_pi_contested, pi, pi_link); 1152161678Sdavidxu} 1153161678Sdavidxu 1154161678Sdavidxu/* 1155161678Sdavidxu * Claim ownership of a PI mutex. 1156161678Sdavidxu */ 1157161678Sdavidxustatic int 1158161678Sdavidxuumtx_pi_claim(struct umtx_pi *pi, struct thread *owner) 1159161678Sdavidxu{ 1160161678Sdavidxu struct umtx_q *uq, *uq_owner; 1161161678Sdavidxu 1162161678Sdavidxu uq_owner = owner->td_umtxq; 1163161678Sdavidxu mtx_lock_spin(&sched_lock); 1164161678Sdavidxu if (pi->pi_owner == owner) { 1165161678Sdavidxu mtx_unlock_spin(&sched_lock); 1166161678Sdavidxu return (0); 1167161678Sdavidxu } 1168161678Sdavidxu 1169161678Sdavidxu if (pi->pi_owner != NULL) { 1170161678Sdavidxu /* 1171161678Sdavidxu * userland may have already messed the mutex, sigh. 1172161678Sdavidxu */ 1173161678Sdavidxu mtx_unlock_spin(&sched_lock); 1174161678Sdavidxu return (EPERM); 1175161678Sdavidxu } 1176161678Sdavidxu umtx_pi_setowner(pi, owner); 1177161678Sdavidxu uq = TAILQ_FIRST(&pi->pi_blocked); 1178161678Sdavidxu if (uq != NULL) { 1179161678Sdavidxu int pri; 1180161678Sdavidxu 1181161678Sdavidxu pri = UPRI(uq->uq_thread); 1182161678Sdavidxu if (pri < UPRI(owner)) 1183161678Sdavidxu sched_lend_user_prio(owner, pri); 1184161678Sdavidxu } 1185161678Sdavidxu mtx_unlock_spin(&sched_lock); 1186161678Sdavidxu return (0); 1187161678Sdavidxu} 1188161678Sdavidxu 1189161678Sdavidxu/* 1190161678Sdavidxu * Adjust a thread's order position in its blocked PI mutex, 1191161678Sdavidxu * this may result new priority propagating process. 1192161678Sdavidxu */ 1193161599Sdavidxuvoid 1194161678Sdavidxuumtx_pi_adjust(struct thread *td, u_char oldpri) 1195161599Sdavidxu{ 1196161678Sdavidxu struct umtx_q *uq; 1197161678Sdavidxu struct umtx_pi *pi; 1198161678Sdavidxu 1199161678Sdavidxu uq = td->td_umtxq; 1200161678Sdavidxu 1201161678Sdavidxu mtx_assert(&sched_lock, MA_OWNED); 1202161678Sdavidxu MPASS(TD_ON_UPILOCK(td)); 1203161678Sdavidxu 1204161678Sdavidxu /* 1205161678Sdavidxu * Pick up the lock that td is blocked on. 1206161678Sdavidxu */ 1207161678Sdavidxu pi = uq->uq_pi_blocked; 1208161678Sdavidxu MPASS(pi != NULL); 1209161678Sdavidxu 1210161678Sdavidxu /* Resort the turnstile on the list. */ 1211161678Sdavidxu if (!umtx_pi_adjust_thread(pi, td)) 1212161678Sdavidxu return; 1213161678Sdavidxu 1214161678Sdavidxu /* 1215161678Sdavidxu * If our priority was lowered and we are at the head of the 1216161678Sdavidxu * turnstile, then propagate our new priority up the chain. 1217161678Sdavidxu */ 1218161678Sdavidxu if (uq == TAILQ_FIRST(&pi->pi_blocked) && UPRI(td) < oldpri) 1219161678Sdavidxu umtx_propagate_priority(td); 1220161599Sdavidxu} 1221161599Sdavidxu 1222161678Sdavidxu/* 1223161678Sdavidxu * Sleep on a PI mutex. 1224161678Sdavidxu */ 1225161678Sdavidxustatic int 1226161678Sdavidxuumtxq_sleep_pi(struct umtx_q *uq, struct umtx_pi *pi, 1227161678Sdavidxu uint32_t owner, const char *wmesg, int timo) 1228161678Sdavidxu{ 1229161678Sdavidxu struct umtxq_chain *uc; 1230161678Sdavidxu struct thread *td, *td1; 1231161678Sdavidxu struct umtx_q *uq1; 1232161678Sdavidxu int pri; 1233161678Sdavidxu int error = 0; 1234161678Sdavidxu 1235161678Sdavidxu td = uq->uq_thread; 1236161678Sdavidxu KASSERT(td == curthread, ("inconsistent uq_thread")); 1237161678Sdavidxu uc = umtxq_getchain(&uq->uq_key); 1238161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 1239161678Sdavidxu umtxq_insert(uq); 1240161678Sdavidxu if (pi->pi_owner == NULL) { 1241161678Sdavidxu /* XXX 1242161678Sdavidxu * Current, We only support process private PI-mutex, 1243161678Sdavidxu * non-contended PI-mutexes are locked in userland. 1244161678Sdavidxu * Process shared PI-mutex should always be initialized 1245161678Sdavidxu * by kernel and be registered in kernel, locking should 1246161678Sdavidxu * always be done by kernel to avoid security problems. 1247161678Sdavidxu * For process private PI-mutex, we can find owner 1248161678Sdavidxu * thread and boost its priority safely. 1249161678Sdavidxu */ 1250161678Sdavidxu PROC_LOCK(curproc); 1251161678Sdavidxu td1 = thread_find(curproc, owner); 1252161678Sdavidxu mtx_lock_spin(&sched_lock); 1253161678Sdavidxu if (td1 != NULL && pi->pi_owner == NULL) { 1254161678Sdavidxu uq1 = td1->td_umtxq; 1255161678Sdavidxu umtx_pi_setowner(pi, td1); 1256161678Sdavidxu } 1257161678Sdavidxu PROC_UNLOCK(curproc); 1258161678Sdavidxu } else { 1259161678Sdavidxu mtx_lock_spin(&sched_lock); 1260161678Sdavidxu } 1261161678Sdavidxu 1262161678Sdavidxu TAILQ_FOREACH(uq1, &pi->pi_blocked, uq_lockq) { 1263161678Sdavidxu pri = UPRI(uq1->uq_thread); 1264161678Sdavidxu if (pri > UPRI(td)) 1265161678Sdavidxu break; 1266161678Sdavidxu } 1267161678Sdavidxu 1268161678Sdavidxu if (uq1 != NULL) 1269161678Sdavidxu TAILQ_INSERT_BEFORE(uq1, uq, uq_lockq); 1270161678Sdavidxu else 1271161678Sdavidxu TAILQ_INSERT_TAIL(&pi->pi_blocked, uq, uq_lockq); 1272161678Sdavidxu 1273161678Sdavidxu uq->uq_pi_blocked = pi; 1274161678Sdavidxu td->td_flags |= TDF_UPIBLOCKED; 1275161678Sdavidxu mtx_unlock_spin(&sched_lock); 1276161678Sdavidxu umtxq_unlock(&uq->uq_key); 1277161678Sdavidxu 1278161678Sdavidxu mtx_lock_spin(&sched_lock); 1279161678Sdavidxu umtx_propagate_priority(td); 1280161678Sdavidxu mtx_unlock_spin(&sched_lock); 1281161678Sdavidxu 1282161678Sdavidxu umtxq_lock(&uq->uq_key); 1283161678Sdavidxu if (uq->uq_flags & UQF_UMTXQ) { 1284161678Sdavidxu error = msleep(uq, &uc->uc_lock, PCATCH, wmesg, timo); 1285161678Sdavidxu if (error == EWOULDBLOCK) 1286161678Sdavidxu error = ETIMEDOUT; 1287161678Sdavidxu if (uq->uq_flags & UQF_UMTXQ) { 1288161678Sdavidxu umtxq_busy(&uq->uq_key); 1289161678Sdavidxu umtxq_remove(uq); 1290161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1291161678Sdavidxu } 1292161678Sdavidxu } 1293161678Sdavidxu umtxq_unlock(&uq->uq_key); 1294161678Sdavidxu 1295161678Sdavidxu mtx_lock_spin(&sched_lock); 1296161678Sdavidxu uq->uq_pi_blocked = NULL; 1297161678Sdavidxu td->td_flags &= ~TDF_UPIBLOCKED; 1298161678Sdavidxu TAILQ_REMOVE(&pi->pi_blocked, uq, uq_lockq); 1299161678Sdavidxu umtx_unpropagate_priority(pi); 1300161678Sdavidxu mtx_unlock_spin(&sched_lock); 1301161678Sdavidxu 1302161678Sdavidxu umtxq_lock(&uq->uq_key); 1303161678Sdavidxu 1304161678Sdavidxu return (error); 1305161678Sdavidxu} 1306161678Sdavidxu 1307161678Sdavidxu/* 1308161678Sdavidxu * Add reference count for a PI mutex. 1309161678Sdavidxu */ 1310161678Sdavidxustatic void 1311161678Sdavidxuumtx_pi_ref(struct umtx_pi *pi) 1312161678Sdavidxu{ 1313161678Sdavidxu struct umtxq_chain *uc; 1314161678Sdavidxu 1315161678Sdavidxu uc = umtxq_getchain(&pi->pi_key); 1316161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 1317161678Sdavidxu pi->pi_refcount++; 1318161678Sdavidxu} 1319161678Sdavidxu 1320161678Sdavidxu/* 1321161678Sdavidxu * Decrease reference count for a PI mutex, if the counter 1322161678Sdavidxu * is decreased to zero, its memory space is freed. 1323161678Sdavidxu */ 1324161678Sdavidxustatic void 1325161678Sdavidxuumtx_pi_unref(struct umtx_pi *pi) 1326161678Sdavidxu{ 1327161678Sdavidxu struct umtxq_chain *uc; 1328161678Sdavidxu int free = 0; 1329161678Sdavidxu 1330161678Sdavidxu uc = umtxq_getchain(&pi->pi_key); 1331161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 1332161678Sdavidxu KASSERT(pi->pi_refcount > 0, ("invalid reference count")); 1333161678Sdavidxu if (--pi->pi_refcount == 0) { 1334161678Sdavidxu mtx_lock_spin(&sched_lock); 1335161678Sdavidxu if (pi->pi_owner != NULL) { 1336161678Sdavidxu TAILQ_REMOVE(&pi->pi_owner->td_umtxq->uq_pi_contested, 1337161678Sdavidxu pi, pi_link); 1338161678Sdavidxu pi->pi_owner = NULL; 1339161678Sdavidxu } 1340161678Sdavidxu KASSERT(TAILQ_EMPTY(&pi->pi_blocked), 1341161678Sdavidxu ("blocked queue not empty")); 1342161678Sdavidxu mtx_unlock_spin(&sched_lock); 1343161678Sdavidxu TAILQ_REMOVE(&uc->uc_pi_list, pi, pi_hashlink); 1344161678Sdavidxu free = 1; 1345161678Sdavidxu } 1346161678Sdavidxu if (free) 1347161678Sdavidxu umtx_pi_free(pi); 1348161678Sdavidxu} 1349161678Sdavidxu 1350161678Sdavidxu/* 1351161678Sdavidxu * Find a PI mutex in hash table. 1352161678Sdavidxu */ 1353161678Sdavidxustatic struct umtx_pi * 1354161678Sdavidxuumtx_pi_lookup(struct umtx_key *key) 1355161678Sdavidxu{ 1356161678Sdavidxu struct umtxq_chain *uc; 1357161678Sdavidxu struct umtx_pi *pi; 1358161678Sdavidxu 1359161678Sdavidxu uc = umtxq_getchain(key); 1360161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 1361161678Sdavidxu 1362161678Sdavidxu TAILQ_FOREACH(pi, &uc->uc_pi_list, pi_hashlink) { 1363161678Sdavidxu if (umtx_key_match(&pi->pi_key, key)) { 1364161678Sdavidxu return (pi); 1365161678Sdavidxu } 1366161678Sdavidxu } 1367161678Sdavidxu return (NULL); 1368161678Sdavidxu} 1369161678Sdavidxu 1370161678Sdavidxu/* 1371161678Sdavidxu * Insert a PI mutex into hash table. 1372161678Sdavidxu */ 1373161678Sdavidxustatic inline void 1374161678Sdavidxuumtx_pi_insert(struct umtx_pi *pi) 1375161678Sdavidxu{ 1376161678Sdavidxu struct umtxq_chain *uc; 1377161678Sdavidxu 1378161678Sdavidxu uc = umtxq_getchain(&pi->pi_key); 1379161678Sdavidxu UMTXQ_LOCKED_ASSERT(uc); 1380161678Sdavidxu TAILQ_INSERT_TAIL(&uc->uc_pi_list, pi, pi_hashlink); 1381161678Sdavidxu} 1382161678Sdavidxu 1383161678Sdavidxu/* 1384161678Sdavidxu * Lock a PI mutex. 1385161678Sdavidxu */ 1386161678Sdavidxustatic int 1387161678Sdavidxu_do_lock_pi(struct thread *td, struct umutex *m, uint32_t flags, int timo, 1388161678Sdavidxu int try) 1389161678Sdavidxu{ 1390161678Sdavidxu struct umtx_q *uq; 1391161678Sdavidxu struct umtx_pi *pi, *new_pi; 1392161678Sdavidxu uint32_t id, owner, old; 1393161678Sdavidxu int error; 1394161678Sdavidxu 1395161678Sdavidxu id = td->td_tid; 1396161678Sdavidxu uq = td->td_umtxq; 1397161678Sdavidxu 1398161678Sdavidxu if ((error = umtx_key_get(m, TYPE_PI_UMUTEX, GET_SHARE(flags), 1399161678Sdavidxu &uq->uq_key)) != 0) 1400161678Sdavidxu return (error); 1401161678Sdavidxu for (;;) { 1402161678Sdavidxu pi = NULL; 1403161678Sdavidxu umtxq_lock(&uq->uq_key); 1404161678Sdavidxu pi = umtx_pi_lookup(&uq->uq_key); 1405161678Sdavidxu if (pi == NULL) { 1406161678Sdavidxu umtxq_unlock(&uq->uq_key); 1407161678Sdavidxu new_pi = umtx_pi_alloc(); 1408161678Sdavidxu new_pi->pi_key = uq->uq_key; 1409161678Sdavidxu umtxq_lock(&uq->uq_key); 1410161678Sdavidxu pi = umtx_pi_lookup(&uq->uq_key); 1411161678Sdavidxu if (pi != NULL) 1412161678Sdavidxu umtx_pi_free(new_pi); 1413161678Sdavidxu else { 1414161678Sdavidxu umtx_pi_insert(new_pi); 1415161678Sdavidxu pi = new_pi; 1416161678Sdavidxu } 1417161678Sdavidxu } 1418161678Sdavidxu 1419161678Sdavidxu umtx_pi_ref(pi); 1420161678Sdavidxu umtxq_unlock(&uq->uq_key); 1421161678Sdavidxu 1422161678Sdavidxu /* 1423161678Sdavidxu * Care must be exercised when dealing with umtx structure. It 1424161678Sdavidxu * can fault on any access. 1425161678Sdavidxu */ 1426161678Sdavidxu 1427161678Sdavidxu /* 1428161678Sdavidxu * Try the uncontested case. This should be done in userland. 1429161678Sdavidxu */ 1430161678Sdavidxu owner = casuword32(&m->m_owner, UMUTEX_UNOWNED, id); 1431161678Sdavidxu 1432161678Sdavidxu /* The acquire succeeded. */ 1433161678Sdavidxu if (owner == UMUTEX_UNOWNED) { 1434161678Sdavidxu error = 0; 1435161678Sdavidxu break; 1436161678Sdavidxu } 1437161678Sdavidxu 1438161678Sdavidxu /* The address was invalid. */ 1439161678Sdavidxu if (owner == -1) { 1440161678Sdavidxu error = EFAULT; 1441161678Sdavidxu break; 1442161678Sdavidxu } 1443161678Sdavidxu 1444161678Sdavidxu /* If no one owns it but it is contested try to acquire it. */ 1445161678Sdavidxu if (owner == UMUTEX_CONTESTED) { 1446161678Sdavidxu owner = casuword32(&m->m_owner, 1447161678Sdavidxu UMUTEX_CONTESTED, id | UMUTEX_CONTESTED); 1448161678Sdavidxu 1449161678Sdavidxu if (owner == UMUTEX_CONTESTED) { 1450161678Sdavidxu umtxq_lock(&uq->uq_key); 1451161678Sdavidxu error = umtx_pi_claim(pi, td); 1452161678Sdavidxu umtxq_unlock(&uq->uq_key); 1453161678Sdavidxu break; 1454161678Sdavidxu } 1455161678Sdavidxu 1456161678Sdavidxu /* The address was invalid. */ 1457161678Sdavidxu if (owner == -1) { 1458161678Sdavidxu error = EFAULT; 1459161678Sdavidxu break; 1460161678Sdavidxu } 1461161678Sdavidxu 1462161678Sdavidxu /* If this failed the lock has changed, restart. */ 1463161678Sdavidxu umtxq_lock(&uq->uq_key); 1464161678Sdavidxu umtx_pi_unref(pi); 1465161678Sdavidxu umtxq_unlock(&uq->uq_key); 1466161678Sdavidxu pi = NULL; 1467161678Sdavidxu continue; 1468161678Sdavidxu } 1469161678Sdavidxu 1470161678Sdavidxu if ((flags & UMUTEX_ERROR_CHECK) != 0 && 1471161678Sdavidxu (owner & ~UMUTEX_CONTESTED) == id) { 1472161678Sdavidxu error = EDEADLK; 1473161678Sdavidxu break; 1474161678Sdavidxu } 1475161678Sdavidxu 1476161678Sdavidxu if (try != 0) { 1477161678Sdavidxu error = EBUSY; 1478161678Sdavidxu break; 1479161678Sdavidxu } 1480161678Sdavidxu 1481161678Sdavidxu /* 1482161678Sdavidxu * If we caught a signal, we have retried and now 1483161678Sdavidxu * exit immediately. 1484161678Sdavidxu */ 1485161678Sdavidxu if (error != 0) 1486161678Sdavidxu break; 1487161678Sdavidxu 1488161678Sdavidxu umtxq_lock(&uq->uq_key); 1489161678Sdavidxu umtxq_busy(&uq->uq_key); 1490161678Sdavidxu umtxq_unlock(&uq->uq_key); 1491161678Sdavidxu 1492161678Sdavidxu /* 1493161678Sdavidxu * Set the contested bit so that a release in user space 1494161678Sdavidxu * knows to use the system call for unlock. If this fails 1495161678Sdavidxu * either some one else has acquired the lock or it has been 1496161678Sdavidxu * released. 1497161678Sdavidxu */ 1498161678Sdavidxu old = casuword32(&m->m_owner, owner, owner | UMUTEX_CONTESTED); 1499161678Sdavidxu 1500161678Sdavidxu /* The address was invalid. */ 1501161678Sdavidxu if (old == -1) { 1502161678Sdavidxu umtxq_lock(&uq->uq_key); 1503161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1504161678Sdavidxu umtxq_unlock(&uq->uq_key); 1505161678Sdavidxu error = EFAULT; 1506161678Sdavidxu break; 1507161678Sdavidxu } 1508161678Sdavidxu 1509161678Sdavidxu umtxq_lock(&uq->uq_key); 1510161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1511161678Sdavidxu /* 1512161678Sdavidxu * We set the contested bit, sleep. Otherwise the lock changed 1513161678Sdavidxu * and we need to retry or we lost a race to the thread 1514161678Sdavidxu * unlocking the umtx. 1515161678Sdavidxu */ 1516161678Sdavidxu if (old == owner) 1517161678Sdavidxu error = umtxq_sleep_pi(uq, pi, owner & ~UMUTEX_CONTESTED, 1518161678Sdavidxu "umtxpi", timo); 1519161678Sdavidxu umtx_pi_unref(pi); 1520161678Sdavidxu umtxq_unlock(&uq->uq_key); 1521161678Sdavidxu pi = NULL; 1522161678Sdavidxu } 1523161678Sdavidxu 1524161678Sdavidxu if (pi != NULL) { 1525161678Sdavidxu umtxq_lock(&uq->uq_key); 1526161678Sdavidxu umtx_pi_unref(pi); 1527161678Sdavidxu umtxq_unlock(&uq->uq_key); 1528161678Sdavidxu } 1529161678Sdavidxu 1530161678Sdavidxu umtx_key_release(&uq->uq_key); 1531161678Sdavidxu return (error); 1532161678Sdavidxu} 1533161678Sdavidxu 1534161678Sdavidxustatic int 1535161678Sdavidxudo_lock_pi(struct thread *td, struct umutex *m, uint32_t flags, 1536161678Sdavidxu struct timespec *timeout, int try) 1537161678Sdavidxu{ 1538161678Sdavidxu struct timespec ts, ts2, ts3; 1539161678Sdavidxu struct timeval tv; 1540161678Sdavidxu int error; 1541161678Sdavidxu 1542161678Sdavidxu if (timeout == NULL) { 1543161678Sdavidxu error = _do_lock_pi(td, m, flags, 0, try); 1544161678Sdavidxu } else { 1545161678Sdavidxu getnanouptime(&ts); 1546161678Sdavidxu timespecadd(&ts, timeout); 1547161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, timeout); 1548161678Sdavidxu for (;;) { 1549161678Sdavidxu error = _do_lock_pi(td, m, flags, tvtohz(&tv), try); 1550161678Sdavidxu if (error != ETIMEDOUT) 1551161678Sdavidxu break; 1552161678Sdavidxu getnanouptime(&ts2); 1553161678Sdavidxu if (timespeccmp(&ts2, &ts, >=)) { 1554161678Sdavidxu error = ETIMEDOUT; 1555161678Sdavidxu break; 1556161678Sdavidxu } 1557161678Sdavidxu ts3 = ts; 1558161678Sdavidxu timespecsub(&ts3, &ts2); 1559161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, &ts3); 1560161678Sdavidxu } 1561161678Sdavidxu } 1562161684Sdavidxu /* Mutex locking is be restarted if it is interrupted. */ 1563161678Sdavidxu if (error == EINTR) 1564161678Sdavidxu error = ERESTART; 1565161678Sdavidxu return (error); 1566161678Sdavidxu} 1567161678Sdavidxu 1568161678Sdavidxu/* 1569161678Sdavidxu * Unlock a PI mutex. 1570161678Sdavidxu */ 1571161678Sdavidxustatic int 1572161678Sdavidxudo_unlock_pi(struct thread *td, struct umutex *m, uint32_t flags) 1573161678Sdavidxu{ 1574161678Sdavidxu struct umtx_key key; 1575161678Sdavidxu struct umtx_q *uq_first, *uq_first2, *uq_me; 1576161678Sdavidxu struct umtx_pi *pi, *pi2; 1577161678Sdavidxu uint32_t owner, old, id; 1578161678Sdavidxu int error; 1579161678Sdavidxu int count; 1580161678Sdavidxu int pri; 1581161678Sdavidxu 1582161678Sdavidxu id = td->td_tid; 1583161678Sdavidxu /* 1584161678Sdavidxu * Make sure we own this mtx. 1585161678Sdavidxu */ 1586161678Sdavidxu owner = fuword32(&m->m_owner); 1587161678Sdavidxu if (owner == -1) 1588161678Sdavidxu return (EFAULT); 1589161678Sdavidxu 1590161678Sdavidxu if ((owner & ~UMUTEX_CONTESTED) != id) 1591161678Sdavidxu return (EPERM); 1592161678Sdavidxu 1593161678Sdavidxu /* This should be done in userland */ 1594161678Sdavidxu if ((owner & UMUTEX_CONTESTED) == 0) { 1595161678Sdavidxu old = casuword32(&m->m_owner, owner, UMUTEX_UNOWNED); 1596161678Sdavidxu if (old == -1) 1597161678Sdavidxu return (EFAULT); 1598161678Sdavidxu if (old == owner) 1599161678Sdavidxu return (0); 1600161855Sdavidxu owner = old; 1601161678Sdavidxu } 1602161678Sdavidxu 1603161678Sdavidxu /* We should only ever be in here for contested locks */ 1604161678Sdavidxu if ((error = umtx_key_get(m, TYPE_PI_UMUTEX, GET_SHARE(flags), 1605161678Sdavidxu &key)) != 0) 1606161678Sdavidxu return (error); 1607161678Sdavidxu 1608161678Sdavidxu umtxq_lock(&key); 1609161678Sdavidxu umtxq_busy(&key); 1610161678Sdavidxu count = umtxq_count_pi(&key, &uq_first); 1611161678Sdavidxu if (uq_first != NULL) { 1612161678Sdavidxu pi = uq_first->uq_pi_blocked; 1613161678Sdavidxu if (pi->pi_owner != curthread) { 1614161678Sdavidxu umtxq_unbusy(&key); 1615161678Sdavidxu umtxq_unlock(&key); 1616161678Sdavidxu /* userland messed the mutex */ 1617161678Sdavidxu return (EPERM); 1618161678Sdavidxu } 1619161678Sdavidxu uq_me = curthread->td_umtxq; 1620161678Sdavidxu mtx_lock_spin(&sched_lock); 1621161678Sdavidxu pi->pi_owner = NULL; 1622161678Sdavidxu TAILQ_REMOVE(&uq_me->uq_pi_contested, pi, pi_link); 1623161678Sdavidxu uq_first = TAILQ_FIRST(&pi->pi_blocked); 1624161678Sdavidxu pri = PRI_MAX; 1625161678Sdavidxu TAILQ_FOREACH(pi2, &uq_me->uq_pi_contested, pi_link) { 1626161678Sdavidxu uq_first2 = TAILQ_FIRST(&pi2->pi_blocked); 1627161678Sdavidxu if (uq_first2 != NULL) { 1628161678Sdavidxu if (pri > UPRI(uq_first2->uq_thread)) 1629161678Sdavidxu pri = UPRI(uq_first2->uq_thread); 1630161678Sdavidxu } 1631161678Sdavidxu } 1632161678Sdavidxu sched_unlend_user_prio(curthread, pri); 1633161678Sdavidxu mtx_unlock_spin(&sched_lock); 1634161678Sdavidxu } 1635161678Sdavidxu umtxq_unlock(&key); 1636161678Sdavidxu 1637161678Sdavidxu /* 1638161678Sdavidxu * When unlocking the umtx, it must be marked as unowned if 1639161678Sdavidxu * there is zero or one thread only waiting for it. 1640161678Sdavidxu * Otherwise, it must be marked as contested. 1641161678Sdavidxu */ 1642161678Sdavidxu old = casuword32(&m->m_owner, owner, 1643161678Sdavidxu count <= 1 ? UMUTEX_UNOWNED : UMUTEX_CONTESTED); 1644161678Sdavidxu 1645161678Sdavidxu umtxq_lock(&key); 1646161678Sdavidxu if (uq_first != NULL) 1647161678Sdavidxu umtxq_signal_thread(uq_first); 1648161678Sdavidxu umtxq_unbusy(&key); 1649161678Sdavidxu umtxq_unlock(&key); 1650161678Sdavidxu umtx_key_release(&key); 1651161678Sdavidxu if (old == -1) 1652161678Sdavidxu return (EFAULT); 1653161678Sdavidxu if (old != owner) 1654161678Sdavidxu return (EINVAL); 1655161678Sdavidxu return (0); 1656161678Sdavidxu} 1657161678Sdavidxu 1658161678Sdavidxu/* 1659161678Sdavidxu * Lock a PP mutex. 1660161678Sdavidxu */ 1661161678Sdavidxustatic int 1662161678Sdavidxu_do_lock_pp(struct thread *td, struct umutex *m, uint32_t flags, int timo, 1663161678Sdavidxu int try) 1664161678Sdavidxu{ 1665161678Sdavidxu struct umtx_q *uq, *uq2; 1666161678Sdavidxu struct umtx_pi *pi; 1667161678Sdavidxu uint32_t ceiling; 1668161678Sdavidxu uint32_t owner, id; 1669161678Sdavidxu int error, pri, old_inherited_pri, su; 1670161678Sdavidxu 1671161678Sdavidxu id = td->td_tid; 1672161678Sdavidxu uq = td->td_umtxq; 1673161678Sdavidxu if ((error = umtx_key_get(m, TYPE_PP_UMUTEX, GET_SHARE(flags), 1674161678Sdavidxu &uq->uq_key)) != 0) 1675161678Sdavidxu return (error); 1676161678Sdavidxu su = (suser(td) == 0); 1677161678Sdavidxu for (;;) { 1678161678Sdavidxu old_inherited_pri = uq->uq_inherited_pri; 1679161678Sdavidxu umtxq_lock(&uq->uq_key); 1680161678Sdavidxu umtxq_busy(&uq->uq_key); 1681161678Sdavidxu umtxq_unlock(&uq->uq_key); 1682161678Sdavidxu 1683161678Sdavidxu ceiling = RTP_PRIO_MAX - fuword32(&m->m_ceilings[0]); 1684161678Sdavidxu if (ceiling > RTP_PRIO_MAX) { 1685161678Sdavidxu error = EINVAL; 1686161678Sdavidxu goto out; 1687161678Sdavidxu } 1688161678Sdavidxu 1689161678Sdavidxu mtx_lock_spin(&sched_lock); 1690161678Sdavidxu if (UPRI(td) < PRI_MIN_REALTIME + ceiling) { 1691161678Sdavidxu mtx_unlock_spin(&sched_lock); 1692161678Sdavidxu error = EINVAL; 1693161678Sdavidxu goto out; 1694161678Sdavidxu } 1695161678Sdavidxu if (su && PRI_MIN_REALTIME + ceiling < uq->uq_inherited_pri) { 1696161678Sdavidxu uq->uq_inherited_pri = PRI_MIN_REALTIME + ceiling; 1697161678Sdavidxu if (uq->uq_inherited_pri < UPRI(td)) 1698161678Sdavidxu sched_lend_user_prio(td, uq->uq_inherited_pri); 1699161678Sdavidxu } 1700161678Sdavidxu mtx_unlock_spin(&sched_lock); 1701161678Sdavidxu 1702161678Sdavidxu owner = casuword32(&m->m_owner, 1703161678Sdavidxu UMUTEX_CONTESTED, id | UMUTEX_CONTESTED); 1704161678Sdavidxu 1705161678Sdavidxu if (owner == UMUTEX_CONTESTED) { 1706161678Sdavidxu error = 0; 1707161678Sdavidxu break; 1708161678Sdavidxu } 1709161678Sdavidxu 1710161678Sdavidxu /* The address was invalid. */ 1711161678Sdavidxu if (owner == -1) { 1712161678Sdavidxu error = EFAULT; 1713161678Sdavidxu break; 1714161678Sdavidxu } 1715161678Sdavidxu 1716161678Sdavidxu if ((flags & UMUTEX_ERROR_CHECK) != 0 && 1717161678Sdavidxu (owner & ~UMUTEX_CONTESTED) == id) { 1718161678Sdavidxu error = EDEADLK; 1719161678Sdavidxu break; 1720161678Sdavidxu } 1721161678Sdavidxu 1722161678Sdavidxu if (try != 0) { 1723161678Sdavidxu error = EBUSY; 1724161678Sdavidxu break; 1725161678Sdavidxu } 1726161678Sdavidxu 1727161678Sdavidxu /* 1728161678Sdavidxu * If we caught a signal, we have retried and now 1729161678Sdavidxu * exit immediately. 1730161678Sdavidxu */ 1731161678Sdavidxu if (error != 0) 1732161678Sdavidxu break; 1733161678Sdavidxu 1734161678Sdavidxu umtxq_lock(&uq->uq_key); 1735161678Sdavidxu umtxq_insert(uq); 1736161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1737161678Sdavidxu error = umtxq_sleep(uq, "umtxpp", timo); 1738161678Sdavidxu umtxq_remove(uq); 1739161678Sdavidxu umtxq_unlock(&uq->uq_key); 1740161678Sdavidxu 1741161678Sdavidxu mtx_lock_spin(&sched_lock); 1742161678Sdavidxu uq->uq_inherited_pri = old_inherited_pri; 1743161678Sdavidxu pri = PRI_MAX; 1744161678Sdavidxu TAILQ_FOREACH(pi, &uq->uq_pi_contested, pi_link) { 1745161678Sdavidxu uq2 = TAILQ_FIRST(&pi->pi_blocked); 1746161678Sdavidxu if (uq2 != NULL) { 1747161678Sdavidxu if (pri > UPRI(uq2->uq_thread)) 1748161678Sdavidxu pri = UPRI(uq2->uq_thread); 1749161678Sdavidxu } 1750161678Sdavidxu } 1751161678Sdavidxu if (pri > uq->uq_inherited_pri) 1752161678Sdavidxu pri = uq->uq_inherited_pri; 1753161678Sdavidxu sched_unlend_user_prio(td, pri); 1754161678Sdavidxu mtx_unlock_spin(&sched_lock); 1755161678Sdavidxu } 1756161678Sdavidxu 1757161678Sdavidxu if (error != 0) { 1758161678Sdavidxu mtx_lock_spin(&sched_lock); 1759161678Sdavidxu uq->uq_inherited_pri = old_inherited_pri; 1760161678Sdavidxu pri = PRI_MAX; 1761161678Sdavidxu TAILQ_FOREACH(pi, &uq->uq_pi_contested, pi_link) { 1762161678Sdavidxu uq2 = TAILQ_FIRST(&pi->pi_blocked); 1763161678Sdavidxu if (uq2 != NULL) { 1764161678Sdavidxu if (pri > UPRI(uq2->uq_thread)) 1765161678Sdavidxu pri = UPRI(uq2->uq_thread); 1766161678Sdavidxu } 1767161678Sdavidxu } 1768161678Sdavidxu if (pri > uq->uq_inherited_pri) 1769161678Sdavidxu pri = uq->uq_inherited_pri; 1770161678Sdavidxu sched_unlend_user_prio(td, pri); 1771161678Sdavidxu mtx_unlock_spin(&sched_lock); 1772161678Sdavidxu } 1773161678Sdavidxu 1774161678Sdavidxuout: 1775161678Sdavidxu umtxq_lock(&uq->uq_key); 1776161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1777161678Sdavidxu umtxq_unlock(&uq->uq_key); 1778161678Sdavidxu umtx_key_release(&uq->uq_key); 1779161678Sdavidxu return (error); 1780161678Sdavidxu} 1781161678Sdavidxu 1782161678Sdavidxu/* 1783161678Sdavidxu * Lock a PP mutex. 1784161678Sdavidxu */ 1785161678Sdavidxustatic int 1786161678Sdavidxudo_lock_pp(struct thread *td, struct umutex *m, uint32_t flags, 1787161678Sdavidxu struct timespec *timeout, int try) 1788161678Sdavidxu{ 1789161678Sdavidxu struct timespec ts, ts2, ts3; 1790161678Sdavidxu struct timeval tv; 1791161678Sdavidxu int error; 1792161678Sdavidxu 1793161678Sdavidxu if (timeout == NULL) { 1794161678Sdavidxu error = _do_lock_pp(td, m, flags, 0, try); 1795161678Sdavidxu } else { 1796161678Sdavidxu getnanouptime(&ts); 1797161678Sdavidxu timespecadd(&ts, timeout); 1798161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, timeout); 1799161678Sdavidxu for (;;) { 1800161678Sdavidxu error = _do_lock_pp(td, m, flags, tvtohz(&tv), try); 1801161678Sdavidxu if (error != ETIMEDOUT) 1802161678Sdavidxu break; 1803161678Sdavidxu getnanouptime(&ts2); 1804161678Sdavidxu if (timespeccmp(&ts2, &ts, >=)) { 1805161678Sdavidxu error = ETIMEDOUT; 1806161678Sdavidxu break; 1807161678Sdavidxu } 1808161678Sdavidxu ts3 = ts; 1809161678Sdavidxu timespecsub(&ts3, &ts2); 1810161678Sdavidxu TIMESPEC_TO_TIMEVAL(&tv, &ts3); 1811161678Sdavidxu } 1812161678Sdavidxu } 1813161684Sdavidxu /* Mutex locking is be restarted if it is interrupted. */ 1814161678Sdavidxu if (error == EINTR) 1815161678Sdavidxu error = ERESTART; 1816161678Sdavidxu return (error); 1817161678Sdavidxu} 1818161678Sdavidxu 1819161678Sdavidxu/* 1820161678Sdavidxu * Unlock a PP mutex. 1821161678Sdavidxu */ 1822161678Sdavidxustatic int 1823161678Sdavidxudo_unlock_pp(struct thread *td, struct umutex *m, uint32_t flags) 1824161678Sdavidxu{ 1825161678Sdavidxu struct umtx_key key; 1826161678Sdavidxu struct umtx_q *uq, *uq2; 1827161678Sdavidxu struct umtx_pi *pi; 1828161678Sdavidxu uint32_t owner, id; 1829161678Sdavidxu uint32_t rceiling; 1830161678Sdavidxu int error, pri, new_inherited_pri; 1831161678Sdavidxu 1832161678Sdavidxu id = td->td_tid; 1833161678Sdavidxu uq = td->td_umtxq; 1834161678Sdavidxu 1835161678Sdavidxu /* 1836161678Sdavidxu * Make sure we own this mtx. 1837161678Sdavidxu */ 1838161678Sdavidxu owner = fuword32(&m->m_owner); 1839161678Sdavidxu if (owner == -1) 1840161678Sdavidxu return (EFAULT); 1841161678Sdavidxu 1842161678Sdavidxu if ((owner & ~UMUTEX_CONTESTED) != id) 1843161678Sdavidxu return (EPERM); 1844161678Sdavidxu 1845161678Sdavidxu error = copyin(&m->m_ceilings[1], &rceiling, sizeof(uint32_t)); 1846161678Sdavidxu if (error != 0) 1847161678Sdavidxu return (error); 1848161678Sdavidxu 1849161678Sdavidxu if (rceiling == -1) 1850161678Sdavidxu new_inherited_pri = PRI_MAX; 1851161678Sdavidxu else { 1852161678Sdavidxu rceiling = RTP_PRIO_MAX - rceiling; 1853161678Sdavidxu if (rceiling > RTP_PRIO_MAX) 1854161678Sdavidxu return (EINVAL); 1855161678Sdavidxu new_inherited_pri = PRI_MIN_REALTIME + rceiling; 1856161678Sdavidxu } 1857161678Sdavidxu 1858161678Sdavidxu if ((error = umtx_key_get(m, TYPE_PP_UMUTEX, GET_SHARE(flags), 1859161678Sdavidxu &key)) != 0) 1860161678Sdavidxu return (error); 1861161678Sdavidxu umtxq_lock(&key); 1862161678Sdavidxu umtxq_busy(&key); 1863161678Sdavidxu umtxq_unlock(&key); 1864161678Sdavidxu /* 1865161678Sdavidxu * For priority protected mutex, always set unlocked state 1866161678Sdavidxu * to UMUTEX_CONTESTED, so that userland always enters kernel 1867161678Sdavidxu * to lock the mutex, it is necessary because thread priority 1868161678Sdavidxu * has to be adjusted for such mutex. 1869161678Sdavidxu */ 1870161678Sdavidxu error = suword32(&m->m_owner, UMUTEX_CONTESTED); 1871161678Sdavidxu 1872161678Sdavidxu umtxq_lock(&key); 1873161678Sdavidxu if (error == 0) 1874161678Sdavidxu umtxq_signal(&key, 1); 1875161678Sdavidxu umtxq_unbusy(&key); 1876161678Sdavidxu umtxq_unlock(&key); 1877161678Sdavidxu 1878161678Sdavidxu if (error == -1) 1879161678Sdavidxu error = EFAULT; 1880161678Sdavidxu else { 1881161678Sdavidxu mtx_lock_spin(&sched_lock); 1882161678Sdavidxu uq->uq_inherited_pri = new_inherited_pri; 1883161678Sdavidxu pri = PRI_MAX; 1884161678Sdavidxu TAILQ_FOREACH(pi, &uq->uq_pi_contested, pi_link) { 1885161678Sdavidxu uq2 = TAILQ_FIRST(&pi->pi_blocked); 1886161678Sdavidxu if (uq2 != NULL) { 1887161678Sdavidxu if (pri > UPRI(uq2->uq_thread)) 1888161678Sdavidxu pri = UPRI(uq2->uq_thread); 1889161678Sdavidxu } 1890161678Sdavidxu } 1891161678Sdavidxu if (pri > uq->uq_inherited_pri) 1892161678Sdavidxu pri = uq->uq_inherited_pri; 1893161678Sdavidxu sched_unlend_user_prio(td, pri); 1894161678Sdavidxu mtx_unlock_spin(&sched_lock); 1895161678Sdavidxu } 1896161678Sdavidxu umtx_key_release(&key); 1897161678Sdavidxu return (error); 1898161678Sdavidxu} 1899161678Sdavidxu 1900161678Sdavidxustatic int 1901161678Sdavidxudo_set_ceiling(struct thread *td, struct umutex *m, uint32_t ceiling, 1902161678Sdavidxu uint32_t *old_ceiling) 1903161678Sdavidxu{ 1904161678Sdavidxu struct umtx_q *uq; 1905161678Sdavidxu uint32_t save_ceiling; 1906161678Sdavidxu uint32_t owner, id; 1907161678Sdavidxu uint32_t flags; 1908161678Sdavidxu int error; 1909161678Sdavidxu 1910161678Sdavidxu flags = fuword32(&m->m_flags); 1911161678Sdavidxu if ((flags & UMUTEX_PRIO_PROTECT) == 0) 1912161678Sdavidxu return (EINVAL); 1913161678Sdavidxu if (ceiling > RTP_PRIO_MAX) 1914161678Sdavidxu return (EINVAL); 1915161678Sdavidxu id = td->td_tid; 1916161678Sdavidxu uq = td->td_umtxq; 1917161678Sdavidxu if ((error = umtx_key_get(m, TYPE_PP_UMUTEX, GET_SHARE(flags), 1918161678Sdavidxu &uq->uq_key)) != 0) 1919161678Sdavidxu return (error); 1920161678Sdavidxu for (;;) { 1921161678Sdavidxu umtxq_lock(&uq->uq_key); 1922161678Sdavidxu umtxq_busy(&uq->uq_key); 1923161678Sdavidxu umtxq_unlock(&uq->uq_key); 1924161678Sdavidxu 1925161678Sdavidxu save_ceiling = fuword32(&m->m_ceilings[0]); 1926161678Sdavidxu 1927161678Sdavidxu owner = casuword32(&m->m_owner, 1928161678Sdavidxu UMUTEX_CONTESTED, id | UMUTEX_CONTESTED); 1929161678Sdavidxu 1930161678Sdavidxu if (owner == UMUTEX_CONTESTED) { 1931161678Sdavidxu suword32(&m->m_ceilings[0], ceiling); 1932161678Sdavidxu suword32(&m->m_owner, UMUTEX_CONTESTED); 1933161678Sdavidxu error = 0; 1934161678Sdavidxu break; 1935161678Sdavidxu } 1936161678Sdavidxu 1937161678Sdavidxu /* The address was invalid. */ 1938161678Sdavidxu if (owner == -1) { 1939161678Sdavidxu error = EFAULT; 1940161678Sdavidxu break; 1941161678Sdavidxu } 1942161678Sdavidxu 1943161678Sdavidxu if ((owner & ~UMUTEX_CONTESTED) == id) { 1944161678Sdavidxu suword32(&m->m_ceilings[0], ceiling); 1945161678Sdavidxu error = 0; 1946161678Sdavidxu break; 1947161678Sdavidxu } 1948161678Sdavidxu 1949161678Sdavidxu /* 1950161678Sdavidxu * If we caught a signal, we have retried and now 1951161678Sdavidxu * exit immediately. 1952161678Sdavidxu */ 1953161678Sdavidxu if (error != 0) 1954161678Sdavidxu break; 1955161678Sdavidxu 1956161678Sdavidxu /* 1957161678Sdavidxu * We set the contested bit, sleep. Otherwise the lock changed 1958161678Sdavidxu * and we need to retry or we lost a race to the thread 1959161678Sdavidxu * unlocking the umtx. 1960161678Sdavidxu */ 1961161678Sdavidxu umtxq_lock(&uq->uq_key); 1962161678Sdavidxu umtxq_insert(uq); 1963161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1964161678Sdavidxu error = umtxq_sleep(uq, "umtxpp", 0); 1965161678Sdavidxu umtxq_remove(uq); 1966161678Sdavidxu umtxq_unlock(&uq->uq_key); 1967161678Sdavidxu } 1968161678Sdavidxu umtxq_lock(&uq->uq_key); 1969161678Sdavidxu if (error == 0) 1970161678Sdavidxu umtxq_signal(&uq->uq_key, INT_MAX); 1971161678Sdavidxu umtxq_unbusy(&uq->uq_key); 1972161678Sdavidxu umtxq_unlock(&uq->uq_key); 1973161678Sdavidxu umtx_key_release(&uq->uq_key); 1974161678Sdavidxu if (error == 0 && old_ceiling != NULL) 1975161678Sdavidxu suword32(old_ceiling, save_ceiling); 1976161678Sdavidxu return (error); 1977161678Sdavidxu} 1978161678Sdavidxu 1979161678Sdavidxu/* 1980161678Sdavidxu * Lock a userland POSIX mutex. 1981161678Sdavidxu */ 1982161678Sdavidxustatic int 1983161678Sdavidxudo_lock_umutex(struct thread *td, struct umutex *m, struct timespec *ts, 1984161678Sdavidxu int try) 1985161678Sdavidxu{ 1986161678Sdavidxu uint32_t flags; 1987161678Sdavidxu 1988161678Sdavidxu flags = fuword32(&m->m_flags); 1989161678Sdavidxu if (flags == -1) 1990161678Sdavidxu return (EFAULT); 1991161678Sdavidxu 1992161742Sdavidxu switch(flags & (UMUTEX_PRIO_INHERIT | UMUTEX_PRIO_PROTECT)) { 1993161742Sdavidxu case 0: 1994161742Sdavidxu return (do_lock_normal(td, m, flags, ts, try)); 1995161742Sdavidxu case UMUTEX_PRIO_INHERIT: 1996161742Sdavidxu return (do_lock_pi(td, m, flags, ts, try)); 1997161742Sdavidxu case UMUTEX_PRIO_PROTECT: 1998161742Sdavidxu return (do_lock_pp(td, m, flags, ts, try)); 1999161742Sdavidxu } 2000161678Sdavidxu 2001161742Sdavidxu return (EINVAL); 2002161678Sdavidxu} 2003161678Sdavidxu 2004161678Sdavidxu/* 2005161678Sdavidxu * Unlock a userland POSIX mutex. 2006161678Sdavidxu */ 2007161678Sdavidxustatic int 2008161678Sdavidxudo_unlock_umutex(struct thread *td, struct umutex *m) 2009161678Sdavidxu{ 2010161678Sdavidxu uint32_t flags; 2011161678Sdavidxu 2012161678Sdavidxu flags = fuword32(&m->m_flags); 2013161678Sdavidxu if (flags == -1) 2014161678Sdavidxu return (EFAULT); 2015161678Sdavidxu 2016161855Sdavidxu switch(flags & (UMUTEX_PRIO_INHERIT | UMUTEX_PRIO_PROTECT)) { 2017161855Sdavidxu case 0: 2018161855Sdavidxu return (do_unlock_normal(td, m, flags)); 2019161855Sdavidxu case UMUTEX_PRIO_INHERIT: 2020161855Sdavidxu return (do_unlock_pi(td, m, flags)); 2021161855Sdavidxu case UMUTEX_PRIO_PROTECT: 2022161855Sdavidxu return (do_unlock_pp(td, m, flags)); 2023161855Sdavidxu } 2024161678Sdavidxu 2025161855Sdavidxu return (EINVAL); 2026161678Sdavidxu} 2027161678Sdavidxu 2028139013Sdavidxuint 2029139013Sdavidxu_umtx_lock(struct thread *td, struct _umtx_lock_args *uap) 2030139013Sdavidxu /* struct umtx *umtx */ 2031139013Sdavidxu{ 2032139013Sdavidxu return _do_lock(td, uap->umtx, td->td_tid, 0); 2033139013Sdavidxu} 2034139013Sdavidxu 2035139013Sdavidxuint 2036139013Sdavidxu_umtx_unlock(struct thread *td, struct _umtx_unlock_args *uap) 2037139013Sdavidxu /* struct umtx *umtx */ 2038139013Sdavidxu{ 2039139013Sdavidxu return do_unlock(td, uap->umtx, td->td_tid); 2040139013Sdavidxu} 2041139013Sdavidxu 2042139013Sdavidxuint 2043139013Sdavidxu_umtx_op(struct thread *td, struct _umtx_op_args *uap) 2044139013Sdavidxu{ 2045140245Sdavidxu struct timespec timeout; 2046139013Sdavidxu struct timespec *ts; 2047139013Sdavidxu int error; 2048139013Sdavidxu 2049139013Sdavidxu switch(uap->op) { 2050161678Sdavidxu case UMTX_OP_MUTEX_LOCK: 2051161678Sdavidxu /* Allow a null timespec (wait forever). */ 2052161678Sdavidxu if (uap->uaddr2 == NULL) 2053161678Sdavidxu ts = NULL; 2054161678Sdavidxu else { 2055161678Sdavidxu error = copyin(uap->uaddr2, &timeout, sizeof(timeout)); 2056161678Sdavidxu if (error != 0) 2057161678Sdavidxu break; 2058161678Sdavidxu if (timeout.tv_nsec >= 1000000000 || 2059161678Sdavidxu timeout.tv_nsec < 0) { 2060161678Sdavidxu error = EINVAL; 2061161678Sdavidxu break; 2062161678Sdavidxu } 2063161678Sdavidxu ts = &timeout; 2064161678Sdavidxu } 2065161678Sdavidxu error = do_lock_umutex(td, uap->obj, ts, 0); 2066161678Sdavidxu break; 2067161678Sdavidxu case UMTX_OP_MUTEX_UNLOCK: 2068161678Sdavidxu error = do_unlock_umutex(td, uap->obj); 2069161678Sdavidxu break; 2070139013Sdavidxu case UMTX_OP_LOCK: 2071139013Sdavidxu /* Allow a null timespec (wait forever). */ 2072139292Sdavidxu if (uap->uaddr2 == NULL) 2073139013Sdavidxu ts = NULL; 2074139013Sdavidxu else { 2075140245Sdavidxu error = copyin(uap->uaddr2, &timeout, sizeof(timeout)); 2076139013Sdavidxu if (error != 0) 2077140102Sdavidxu break; 2078140245Sdavidxu if (timeout.tv_nsec >= 1000000000 || 2079140245Sdavidxu timeout.tv_nsec < 0) { 2080140102Sdavidxu error = EINVAL; 2081140102Sdavidxu break; 2082140102Sdavidxu } 2083140245Sdavidxu ts = &timeout; 2084139013Sdavidxu } 2085161678Sdavidxu error = do_lock(td, uap->obj, uap->val, ts); 2086140102Sdavidxu break; 2087139013Sdavidxu case UMTX_OP_UNLOCK: 2088161678Sdavidxu error = do_unlock(td, uap->obj, uap->val); 2089140102Sdavidxu break; 2090139427Sdavidxu case UMTX_OP_WAIT: 2091139013Sdavidxu /* Allow a null timespec (wait forever). */ 2092139292Sdavidxu if (uap->uaddr2 == NULL) 2093139013Sdavidxu ts = NULL; 2094139013Sdavidxu else { 2095140245Sdavidxu error = copyin(uap->uaddr2, &timeout, sizeof(timeout)); 2096139013Sdavidxu if (error != 0) 2097140102Sdavidxu break; 2098140245Sdavidxu if (timeout.tv_nsec >= 1000000000 || 2099140245Sdavidxu timeout.tv_nsec < 0) { 2100140102Sdavidxu error = EINVAL; 2101140102Sdavidxu break; 2102140102Sdavidxu } 2103140245Sdavidxu ts = &timeout; 2104139013Sdavidxu } 2105161678Sdavidxu error = do_wait(td, uap->obj, uap->val, ts); 2106140102Sdavidxu break; 2107139013Sdavidxu case UMTX_OP_WAKE: 2108161678Sdavidxu error = kern_umtx_wake(td, uap->obj, uap->val); 2109140102Sdavidxu break; 2110161742Sdavidxu case UMTX_OP_MUTEX_TRYLOCK: 2111161742Sdavidxu error = do_lock_umutex(td, uap->obj, NULL, 1); 2112161742Sdavidxu break; 2113161742Sdavidxu case UMTX_OP_SET_CEILING: 2114161742Sdavidxu error = do_set_ceiling(td, uap->obj, uap->val, uap->uaddr1); 2115161742Sdavidxu break; 2116139013Sdavidxu default: 2117140102Sdavidxu error = EINVAL; 2118140102Sdavidxu break; 2119139013Sdavidxu } 2120140421Sdavidxu return (error); 2121139013Sdavidxu} 2122161678Sdavidxu 2123161678Sdavidxuvoid 2124161678Sdavidxuumtx_thread_init(struct thread *td) 2125161678Sdavidxu{ 2126161678Sdavidxu td->td_umtxq = umtxq_alloc(); 2127161678Sdavidxu td->td_umtxq->uq_thread = td; 2128161678Sdavidxu} 2129161678Sdavidxu 2130161678Sdavidxuvoid 2131161678Sdavidxuumtx_thread_fini(struct thread *td) 2132161678Sdavidxu{ 2133161678Sdavidxu umtxq_free(td->td_umtxq); 2134161678Sdavidxu} 2135161678Sdavidxu 2136161678Sdavidxu/* 2137161678Sdavidxu * It will be called when new thread is created, e.g fork(). 2138161678Sdavidxu */ 2139161678Sdavidxuvoid 2140161678Sdavidxuumtx_thread_alloc(struct thread *td) 2141161678Sdavidxu{ 2142161678Sdavidxu struct umtx_q *uq; 2143161678Sdavidxu 2144161678Sdavidxu uq = td->td_umtxq; 2145161678Sdavidxu uq->uq_inherited_pri = PRI_MAX; 2146161678Sdavidxu 2147161678Sdavidxu KASSERT(uq->uq_flags == 0, ("uq_flags != 0")); 2148161678Sdavidxu KASSERT(uq->uq_thread == td, ("uq_thread != td")); 2149161678Sdavidxu KASSERT(uq->uq_pi_blocked == NULL, ("uq_pi_blocked != NULL")); 2150161678Sdavidxu KASSERT(TAILQ_EMPTY(&uq->uq_pi_contested), ("uq_pi_contested is not empty")); 2151161678Sdavidxu} 2152161678Sdavidxu 2153161678Sdavidxu/* 2154161678Sdavidxu * exec() hook. 2155161678Sdavidxu */ 2156161678Sdavidxustatic void 2157161678Sdavidxuumtx_exec_hook(void *arg __unused, struct proc *p __unused, 2158161678Sdavidxu struct image_params *imgp __unused) 2159161678Sdavidxu{ 2160161678Sdavidxu umtx_thread_cleanup(curthread); 2161161678Sdavidxu} 2162161678Sdavidxu 2163161678Sdavidxu/* 2164161678Sdavidxu * thread_exit() hook. 2165161678Sdavidxu */ 2166161678Sdavidxuvoid 2167161678Sdavidxuumtx_thread_exit(struct thread *td) 2168161678Sdavidxu{ 2169161678Sdavidxu umtx_thread_cleanup(td); 2170161678Sdavidxu} 2171161678Sdavidxu 2172161678Sdavidxu/* 2173161678Sdavidxu * clean up umtx data. 2174161678Sdavidxu */ 2175161678Sdavidxustatic void 2176161678Sdavidxuumtx_thread_cleanup(struct thread *td) 2177161678Sdavidxu{ 2178161678Sdavidxu struct umtx_q *uq; 2179161678Sdavidxu struct umtx_pi *pi; 2180161678Sdavidxu 2181161678Sdavidxu if ((uq = td->td_umtxq) == NULL) 2182161678Sdavidxu return; 2183161678Sdavidxu 2184161678Sdavidxu mtx_lock_spin(&sched_lock); 2185161678Sdavidxu uq->uq_inherited_pri = PRI_MAX; 2186161678Sdavidxu while ((pi = TAILQ_FIRST(&uq->uq_pi_contested)) != NULL) { 2187161678Sdavidxu pi->pi_owner = NULL; 2188161678Sdavidxu TAILQ_REMOVE(&uq->uq_pi_contested, pi, pi_link); 2189161678Sdavidxu } 2190161678Sdavidxu td->td_flags &= ~TDF_UBORROWING; 2191161678Sdavidxu mtx_unlock_spin(&sched_lock); 2192161678Sdavidxu} 2193