1 /* 2 * Copyright (c) 2022 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29 #pragma once 30 31 #include <kern/thread.h> 32 33 #include <os/atomic.h> 34 35 #include <stdint.h> 36 #include <stdbool.h> 37 38 /* 39 * Epoch WAIT/WAKE blocking. 40 * 41 * Epoch WAIT/WAKE provides a generic means to block and unblock on a turnstile 42 * wait queue with minimal knowledge of the underlying synchronization 43 * implementation. This is useful in environments where it's not possible to 44 * block (like exclaves). No direct access to the memory backing the 45 * synchronization primitive is required (in fact, the exclaves security model 46 * explicitly forbids this). 47 * Instead, an epoch is passed which provides an ordering and hence a means to 48 * detect out-of-order WAITs/WAKEs. 49 * 50 * An epoch is a counter value. The epoch is incremented before calling WAKE 51 * (for example when releasing a lock). Each ID maps to an associated counter. 52 * Counters may be shared by multiple IDs. To avoid blocking when an owner has 53 * already released its resource, the epoch is checked for freshness. A stale 54 * epoch causes WAIT to immediately return without blocking. Too much sharing of 55 * counters can cause waiters to return when they could have blocked. 56 * 57 * There are two major constraints for callers of these APIs: 58 * 59 * 1. The kernel's idea of the owning thread must be kept up-to-date 60 * 61 * - On WAIT, the owner becomes the thread described by 'owner'. 62 * - On WAKE, the owner is the woken thread (iff it's not the last waiter) 63 * 64 * If the owning thread doesn't call back into WAIT or WAKE again it may run 65 * with elevated privileges when it shouldn't. 66 * 67 * 2. WAITs which result in a thread blocking must be woken with WAKE 68 * This one is somewhat obvious. The only way for a thread blocked in WAIT to 69 * come back is to be woken with a WAKE. Pre-posted wakes (wakes when there is 70 * no waiter) or stale waits (out of date epoch) return immediately. 71 * 72 */ 73 74 __BEGIN_DECLS 75 76 struct ht; 77 78 /* 79 * @struct esync_queue_ht 80 * 81 * @abstract Identifies the epoch sync space of synchronization objects 82 * associated with "queues". 83 */ 84 extern struct ht esync_queue_ht; 85 86 /* 87 * @struct esync_thread_ht 88 * 89 * @abstract Identifies the epoch sync space of synchronization objects 90 * associated with "threads". 91 */ 92 extern struct ht esync_thread_ht; 93 94 /*! 95 * @enum esync_policy_t 96 * 97 * @abstract Constants defining the policy associated with a synchronization 98 * object. 99 * 100 * @constant ESYNC_POLICY_NONE 101 * Unspecified. 102 * 103 * @constant ESYNC_POLICY_USER 104 * User. 105 * 106 * @constant ESYNC_POLICY_KERNEL 107 * Kernel. 108 */ 109 typedef enum __enum_closed { 110 ESYNC_POLICY_NONE = 0, 111 ESYNC_POLICY_USER = 1, 112 ESYNC_POLICY_KERNEL = 2, 113 } esync_policy_t; 114 115 /*! 116 * @function esync_wait 117 * 118 * @abstract 119 * Wait on a turnstile associated with the specified id 120 * 121 * @param ns 122 * Namespace in which 'id' lives 123 * 124 * @param id 125 * Synchronization object identifier 126 * 127 * @param epoch 128 * Latest epoch of the synchronization object 129 * 130 * @param owner_ctid 131 * Owner of the synchronization object 132 * 133 * @param interruptible 134 * Interruptible flag 135 * 136 * @param policy 137 * A user or kernel synchronization object 138 * 139 * @return 140 * Result of blocking call (or THREAD_NOT_WAITING for pre-posted waits) 141 */ 142 extern wait_result_t esync_wait(struct ht *ns, uint64_t id, uint64_t epoch, 143 os_atomic(uint64_t) * counter, ctid_t owner_ctid, esync_policy_t policy, 144 wait_interrupt_t interruptible); 145 146 /*! 147 * @enum esync_wake_mode_t 148 * 149 * @abstract Constants defining modes for esync_wake 150 * 151 * @constant ESYNC_WAKE_ONE 152 * Wake a single waiter 153 * 154 * @constant ESYNC_WAKE_ALL 155 * Wake all waiters 156 * 157 * @constant ESYNC_WAKE_ONE_WITH_OWNER 158 * Wake a single owner and identify the new owner 159 * 160 * @constant ESYNC_WAKE_THREAD 161 * Wake the specified thread. There is no new owner. 162 */ 163 typedef enum __enum_closed { 164 ESYNC_WAKE_ONE = 1, 165 ESYNC_WAKE_ALL = 2, 166 ESYNC_WAKE_ONE_WITH_OWNER = 3, 167 ESYNC_WAKE_THREAD = 4, 168 } esync_wake_mode_t; 169 170 /*! 171 * @function esync_wake 172 * 173 * @abstract 174 * Wake one or more threads which have blocked on the specified id in esync_wait 175 * 176 * @param ns 177 * Namespace in which 'id' lives 178 * 179 * @param id 180 * Synchronization object identifier 181 * 182 * @param epoch 183 * Latest epoch of the synchronization object 184 * 185 * @param mode 186 * Type of wake to perform. All, one or one with specified owner (new 187 * inheritor). 188 * 189 * @param ctid 190 * Thread identifier. Can identifier the new owner (ESYNC_WAKE_ONE_WITH_OWNER) 191 * or the thread to be woken (ESYNC_WAKE_THREAD). 192 * 193 * @return 194 * KERN_SUCCESS or KERN_NOT_WAITING if no thread was woken 195 */ 196 extern kern_return_t esync_wake(struct ht *ns, uint64_t id, uint64_t epoch, 197 os_atomic(uint64_t) * counter, esync_wake_mode_t mode, 198 ctid_t ctid); 199 200 __END_DECLS 201