1 /* 2 * Copyright (c) 2020 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 #ifndef _SYS_EXISLOCK2_H_ 35 #define _SYS_EXISLOCK2_H_ 36 37 #ifndef _SYS_GLOBALDATA_H_ 38 #include <sys/globaldata.h> 39 #endif 40 #ifndef _MACHINE_THREAD_H_ 41 #include <machine/thread.h> 42 #endif 43 44 /* 45 * Initialize the structure 46 */ 47 static __inline void 48 exis_init(exislock_t *xlk) 49 { 50 xlk->pseudo_ticks = 0; 51 } 52 53 /* 54 * pcpu exis lock API. Enter and and exit a type-safe critical section. 55 */ 56 static __inline void 57 exis_hold_gd(globaldata_t gd) 58 { 59 ++gd->gd_exislockcnt; 60 } 61 62 static __inline void 63 exis_drop_gd(globaldata_t gd) 64 { 65 if (--gd->gd_exislockcnt == 0) 66 gd->gd_exisarmed = 1; 67 } 68 69 static __inline void 70 exis_hold(void) 71 { 72 exis_hold_gd(mycpu); 73 } 74 75 static __inline void 76 exis_drop(void) 77 { 78 exis_drop_gd(mycpu); 79 } 80 81 /* 82 * poll whether the object is usable or not. A value >= 0 indicates that 83 * the (possibly cached) object is usable. 84 * 85 * This call returns the approximate number of pseudo_ticks remaining until 86 * the object becomes unusable, +/- one. 87 * 88 * The actual value returns is either >= 0, or a negative number. Caller 89 * should refrain from trying to interpret values >= 0 other than the fact 90 * that they are >= 0. 91 * 92 * Negative numbers indicate the number of pseudo_ticks which have occurred 93 * since the object became unusable. Various negative values trigger 94 * different actions. 95 */ 96 static __inline long 97 exis_poll(exislock_t *xlk) 98 { 99 long val = xlk->pseudo_ticks; 100 101 cpu_ccfence(); 102 if (val == 0) 103 return val; 104 return (val - pseudo_ticks); 105 } 106 107 /* 108 * Return the current state. Note that the NOTCACHED state persists for 109 * two pseudo_ticks. This is done because the global pseudo_ticks counter 110 * can concurrently increment by 1 (but no more than 1) during a type-safe 111 * critical section. 112 * 113 * The state can transition even while holding a type-safe critical section, 114 * but sequencing is designed such that this does not cause any problems. 115 */ 116 static __inline int 117 exis_state(exislock_t *xlk) 118 { 119 long val = xlk->pseudo_ticks; 120 121 cpu_ccfence(); 122 if (val == 0) 123 return EXIS_LIVE; 124 val = val - pseudo_ticks; 125 if (val >= 0) 126 return EXIS_CACHED; 127 if (val >= -2) 128 return EXIS_NOTCACHED; 129 return EXIS_TERMINATE; 130 } 131 132 /* 133 * Returns non-zero if the structure is usable (either LIVE or CACHED). 134 * 135 * WARNING! The structure is not considered to be usable if it is in 136 * an UNCACHED state, but if it is CACHED and transitions to 137 * UNCACHED during a type-safe critical section it does remain 138 * usable for the duration of that type-safe critical section. 139 */ 140 static __inline int 141 exis_usable(exislock_t *xlk) 142 { 143 return (exis_poll(xlk) >= 0); 144 } 145 146 /* 147 * Returns non-zero if the structure can be destroyed 148 */ 149 static __inline int 150 exis_freeable(exislock_t *xlk) 151 { 152 return (exis_poll(xlk) <= -2); 153 } 154 155 /* 156 * If the structure is in a LIVE or CACHED state, or if it was CACHED and 157 * concurrently transitioned to NOTCACHED in the same type-safe critical 158 * section, the state will be reset to a CACHED(n) state and non-zero is 159 * returned. 160 * 161 * Otherwise 0 is returned and no action is taken. 162 */ 163 static __inline int 164 exis_cache(exislock_t *xlk, long n) 165 { 166 long val = xlk->pseudo_ticks; 167 long pticks = pseudo_ticks; 168 169 cpu_ccfence(); 170 if (val) 171 val = val - pticks; 172 if (val >= -1) { 173 /* 174 * avoid cache line ping-pong 175 */ 176 pticks += n + 1; 177 if (xlk->pseudo_ticks != pticks) { 178 cpu_ccfence(); 179 xlk->pseudo_ticks = pticks; 180 } 181 return 1; 182 } 183 return 0; 184 } 185 186 /* 187 * Termination sequencing. 188 * 189 * The structure is placed in a CACHED(0) state if LIVE or CACHED. 190 * The NOTCACHED state should not be acted upon by the caller until 191 * and unless it transitions to TERMINATE. 192 * 193 * Upon returning EXIS_TERMINATE, the structure is returned to a 194 * NOTCACHED state and another 1-2 pseudo ticks will pass until it goes 195 * back to EXIS_TERMINATE (if needed by the caller). Once the caller 196 * is fully satisfied, it may repurpose or destroy the structure. 197 * 198 * Caller should hold a strong interlock on the structure in addition 199 * to being in a type-safe critical section. 200 */ 201 static __inline exis_state_t 202 exis_terminate(exislock_t *xlk) 203 { 204 exis_state_t state; 205 206 state = exis_state(xlk); 207 switch(state) { 208 case EXIS_TERMINATE: 209 /* 210 * Set to NOTCACHED state and return EXIS_TERMINATE. 211 * due to pseudo_ticks races, the NOTCACHED state will 212 * persist for 1-2 pseudo ticks. 213 */ 214 xlk->pseudo_ticks = pseudo_ticks - 1; 215 state = EXIS_TERMINATE; 216 break; 217 case EXIS_NOTCACHED: 218 break; 219 case EXIS_CACHED: 220 case EXIS_LIVE: 221 xlk->pseudo_ticks = pseudo_ticks; 222 break; 223 } 224 return state; 225 } 226 227 #endif /* !_SYS_EXISLOCK2_H_ */ 228