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. To reduce confusion we also have exis_setlive() 46 * when repurposing a structure, which does the same thing. 47 */ 48 static __inline void 49 exis_init(exislock_t *xlk) 50 { 51 xlk->pseudo_ticks = 0; 52 } 53 54 static __inline void 55 exis_setlive(exislock_t *xlk) 56 { 57 xlk->pseudo_ticks = 0; 58 } 59 60 /* 61 * pcpu exis lock API. Enter and and exit a type-safe critical section. 62 */ 63 static __inline void 64 exis_hold_gd(globaldata_t gd) 65 { 66 ++gd->gd_exislockcnt; 67 } 68 69 static __inline void 70 exis_drop_gd(globaldata_t gd) 71 { 72 if (--gd->gd_exislockcnt == 0) 73 gd->gd_exisarmed = 1; 74 } 75 76 static __inline void 77 exis_hold(void) 78 { 79 exis_hold_gd(mycpu); 80 } 81 82 static __inline void 83 exis_drop(void) 84 { 85 exis_drop_gd(mycpu); 86 } 87 88 /* 89 * poll whether the object is usable or not. A value >= 0 indicates that 90 * the (possibly cached) object is usable. 91 * 92 * This call returns the approximate number of pseudo_ticks remaining until 93 * the object becomes unusable, +/- one. 94 * 95 * The actual value returns is either >= 0, or a negative number. Caller 96 * should refrain from trying to interpret values >= 0 other than the fact 97 * that they are >= 0. 98 * 99 * Negative numbers indicate the number of pseudo_ticks which have occurred 100 * since the object became unusable. Various negative values trigger 101 * different actions. 102 */ 103 static __inline long 104 exis_poll(exislock_t *xlk) 105 { 106 long val = xlk->pseudo_ticks; 107 108 cpu_ccfence(); 109 if (val == 0) 110 return val; 111 return (val - pseudo_ticks); 112 } 113 114 /* 115 * Return the current state. Note that the NOTCACHED state persists for 116 * two pseudo_ticks. This is done because the global pseudo_ticks counter 117 * can concurrently increment by 1 (but no more than 1) during a type-safe 118 * critical section. 119 * 120 * The state can transition even while holding a type-safe critical section, 121 * but sequencing is designed such that this does not cause any problems. 122 */ 123 static __inline int 124 exis_state(exislock_t *xlk) 125 { 126 long val = xlk->pseudo_ticks; 127 128 cpu_ccfence(); 129 if (val == 0) 130 return EXIS_LIVE; 131 val = val - pseudo_ticks; 132 if (val >= 0) 133 return EXIS_CACHED; 134 if (val >= -2) 135 return EXIS_NOTCACHED; 136 return EXIS_TERMINATE; 137 } 138 139 /* 140 * Returns non-zero if the structure is usable (either LIVE or CACHED). 141 * 142 * WARNING! The structure is not considered to be usable if it is in 143 * an UNCACHED state, but if it is CACHED and transitions to 144 * UNCACHED during a type-safe critical section it does remain 145 * usable for the duration of that type-safe critical section. 146 */ 147 static __inline int 148 exis_usable(exislock_t *xlk) 149 { 150 return (exis_poll(xlk) >= 0); 151 } 152 153 /* 154 * Returns non-zero if the structure can be destroyed 155 */ 156 static __inline int 157 exis_freeable(exislock_t *xlk) 158 { 159 return (exis_poll(xlk) <= -2); 160 } 161 162 /* 163 * If the structure is in a LIVE or CACHED state, or if it was CACHED and 164 * concurrently transitioned to NOTCACHED in the same type-safe critical 165 * section, the state will be reset to a CACHED(n) state and non-zero is 166 * returned. 167 * 168 * Otherwise 0 is returned and no action is taken. 169 */ 170 static __inline int 171 exis_cache(exislock_t *xlk, long n) 172 { 173 long val = xlk->pseudo_ticks; 174 long pticks = pseudo_ticks; 175 176 cpu_ccfence(); 177 if (val) 178 val = val - pticks; 179 if (val >= -1) { 180 /* 181 * avoid cache line ping-pong 182 */ 183 pticks += n + 1; 184 if (xlk->pseudo_ticks != pticks) { 185 cpu_ccfence(); 186 xlk->pseudo_ticks = pticks; 187 } 188 return 1; 189 } 190 return 0; 191 } 192 193 /* 194 * The current state of the structure is ignored and the srtucture is 195 * placed in a CACHED(0) state. It will automatically sequence through 196 * the NOTCACHED and TERMINATE states as psuedo_ticks increments. 197 * 198 * The NOTCACHED state is an indeterminant state, since the pseudo_ticks 199 * counter might already be armed for increment, it can increment at least 200 * once while code is inside an exis_hold(). The TERMINATE state occurs 201 * at the second tick. 202 * 203 * If the caller repurposes the structure, it is usually a good idea to 204 * place it back into a LIVE state by calling exis_setlive(). 205 */ 206 static __inline void 207 exis_terminate(exislock_t *xlk) 208 { 209 xlk->pseudo_ticks = pseudo_ticks - 1; 210 } 211 212 #endif /* !_SYS_EXISLOCK2_H_ */ 213