sctp_callout.c (6568B)
1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 2001-2007, by Cisco Systems, Inc. All rights reserved. 5 * Copyright (c) 2008-2012, by Randall Stewart. All rights reserved. 6 * Copyright (c) 2008-2012, by Michael Tuexen. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * a) Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * b) Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the distribution. 17 * 18 * c) Neither the name of Cisco Systems, Inc. nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 32 * THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #if defined(__Userspace__) 36 #include <sys/types.h> 37 #if !defined(_WIN32) 38 #include <sys/wait.h> 39 #include <unistd.h> 40 #include <pthread.h> 41 #endif 42 #if defined(__native_client__) 43 #include <sys/select.h> 44 #endif 45 #include <stdlib.h> 46 #include <string.h> 47 #include <stdio.h> 48 #include <errno.h> 49 #include <user_atomic.h> 50 #include <netinet/sctp_sysctl.h> 51 #include <netinet/sctp_pcb.h> 52 #else 53 #include <netinet/sctp_os.h> 54 #include <netinet/sctp_callout.h> 55 #include <netinet/sctp_pcb.h> 56 #endif 57 #include <netinet/sctputil.h> 58 59 /* 60 * Callout/Timer routines for OS that doesn't have them 61 */ 62 #if defined(__APPLE__) || defined(__Userspace__) 63 static uint32_t ticks = 0; 64 #else 65 extern int ticks; 66 #endif 67 68 uint32_t sctp_get_tick_count(void) { 69 uint32_t ret; 70 71 SCTP_TIMERQ_LOCK(); 72 ret = ticks; 73 SCTP_TIMERQ_UNLOCK(); 74 return ret; 75 } 76 77 /* 78 * SCTP_TIMERQ_LOCK protects: 79 * - SCTP_BASE_INFO(callqueue) 80 * - sctp_os_timer_next: next timer to check 81 */ 82 static sctp_os_timer_t *sctp_os_timer_next = NULL; 83 84 void 85 sctp_os_timer_init(sctp_os_timer_t *c) 86 { 87 memset(c, 0, sizeof(*c)); 88 } 89 90 int 91 sctp_os_timer_start(sctp_os_timer_t *c, uint32_t to_ticks, void (*ftn) (void *), 92 void *arg) 93 { 94 int ret = 0; 95 96 /* paranoia */ 97 if ((c == NULL) || (ftn == NULL)) 98 return (ret); 99 100 SCTP_TIMERQ_LOCK(); 101 /* check to see if we're rescheduling a timer */ 102 if (c->c_flags & SCTP_CALLOUT_PENDING) { 103 ret = 1; 104 if (c == sctp_os_timer_next) { 105 sctp_os_timer_next = TAILQ_NEXT(c, tqe); 106 } 107 TAILQ_REMOVE(&SCTP_BASE_INFO(callqueue), c, tqe); 108 /* 109 * part of the normal "stop a pending callout" process 110 * is to clear the CALLOUT_ACTIVE and CALLOUT_PENDING 111 * flags. We don't bother since we are setting these 112 * below and we still hold the lock. 113 */ 114 } 115 116 /* 117 * We could unlock/splx here and lock/spl at the TAILQ_INSERT_TAIL, 118 * but there's no point since doing this setup doesn't take much time. 119 */ 120 if (to_ticks == 0) 121 to_ticks = 1; 122 123 c->c_arg = arg; 124 c->c_flags = (SCTP_CALLOUT_ACTIVE | SCTP_CALLOUT_PENDING); 125 c->c_func = ftn; 126 c->c_time = ticks + to_ticks; 127 TAILQ_INSERT_TAIL(&SCTP_BASE_INFO(callqueue), c, tqe); 128 SCTP_TIMERQ_UNLOCK(); 129 return (ret); 130 } 131 132 int 133 sctp_os_timer_stop(sctp_os_timer_t *c) 134 { 135 SCTP_TIMERQ_LOCK(); 136 /* 137 * Don't attempt to delete a callout that's not on the queue. 138 */ 139 if ((c->c_flags & SCTP_CALLOUT_PENDING) == 0) { 140 c->c_flags &= ~SCTP_CALLOUT_ACTIVE; 141 SCTP_TIMERQ_UNLOCK(); 142 return (0); 143 } 144 c->c_flags &= ~(SCTP_CALLOUT_ACTIVE | SCTP_CALLOUT_PENDING); 145 if (c == sctp_os_timer_next) { 146 sctp_os_timer_next = TAILQ_NEXT(c, tqe); 147 } 148 TAILQ_REMOVE(&SCTP_BASE_INFO(callqueue), c, tqe); 149 SCTP_TIMERQ_UNLOCK(); 150 return (1); 151 } 152 153 void 154 sctp_handle_tick(uint32_t elapsed_ticks) 155 { 156 sctp_os_timer_t *c; 157 void (*c_func)(void *); 158 void *c_arg; 159 160 SCTP_TIMERQ_LOCK(); 161 /* update our tick count */ 162 ticks += elapsed_ticks; 163 c = TAILQ_FIRST(&SCTP_BASE_INFO(callqueue)); 164 while (c) { 165 if (SCTP_UINT32_GE(ticks, c->c_time)) { 166 sctp_os_timer_next = TAILQ_NEXT(c, tqe); 167 TAILQ_REMOVE(&SCTP_BASE_INFO(callqueue), c, tqe); 168 c_func = c->c_func; 169 c_arg = c->c_arg; 170 c->c_flags &= ~SCTP_CALLOUT_PENDING; 171 SCTP_TIMERQ_UNLOCK(); 172 c_func(c_arg); 173 SCTP_TIMERQ_LOCK(); 174 c = sctp_os_timer_next; 175 } else { 176 c = TAILQ_NEXT(c, tqe); 177 } 178 } 179 sctp_os_timer_next = NULL; 180 SCTP_TIMERQ_UNLOCK(); 181 } 182 183 #if defined(__APPLE__) && !defined(__Userspace__) 184 void 185 sctp_timeout(void *arg SCTP_UNUSED) 186 { 187 sctp_handle_tick(SCTP_BASE_VAR(sctp_main_timer_ticks)); 188 sctp_start_main_timer(); 189 } 190 #endif 191 192 #if defined(__Userspace__) 193 #define TIMEOUT_INTERVAL 10 194 195 void * 196 user_sctp_timer_iterate(void *arg) 197 { 198 sctp_userspace_set_threadname("SCTP timer"); 199 for (;;) { 200 #if defined(_WIN32) 201 Sleep(TIMEOUT_INTERVAL); 202 #else 203 struct timespec amount, remaining; 204 205 remaining.tv_sec = 0; 206 remaining.tv_nsec = TIMEOUT_INTERVAL * 1000 * 1000; 207 do { 208 amount = remaining; 209 } while (nanosleep(&amount, &remaining) == -1); 210 #endif 211 if (atomic_cmpset_int(&SCTP_BASE_VAR(timer_thread_should_exit), 1, 1)) { 212 break; 213 } 214 sctp_handle_tick(sctp_msecs_to_ticks(TIMEOUT_INTERVAL)); 215 } 216 return (NULL); 217 } 218 219 void 220 sctp_start_timer_thread(void) 221 { 222 /* 223 * No need to do SCTP_TIMERQ_LOCK_INIT(); 224 * here, it is being done in sctp_pcb_init() 225 */ 226 int rc; 227 228 rc = sctp_userspace_thread_create(&SCTP_BASE_VAR(timer_thread), user_sctp_timer_iterate); 229 if (rc) { 230 SCTP_PRINTF("ERROR; return code from sctp_thread_create() is %d\n", rc); 231 } else { 232 SCTP_BASE_VAR(timer_thread_started) = 1; 233 } 234 } 235 236 void 237 sctp_stop_timer_thread(void) 238 { 239 atomic_cmpset_int(&SCTP_BASE_VAR(timer_thread_should_exit), 0, 1); 240 if (SCTP_BASE_VAR(timer_thread_started)) { 241 #if defined(_WIN32) 242 WaitForSingleObject(SCTP_BASE_VAR(timer_thread), INFINITE); 243 CloseHandle(SCTP_BASE_VAR(timer_thread)); 244 #else 245 pthread_join(SCTP_BASE_VAR(timer_thread), NULL); 246 #endif 247 } 248 } 249 #endif