periodic_event.c (12107B)
1 2 /* 3 * 4 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 5 * 6 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 7 * 8 * File: periodic_event.c 9 * Purpose: Generic mechanisem for periodic events. 10 */ 11 12 #include <shared/periodic_event.h> 13 #include <sal/core/sync.h> 14 #include <sal/core/thread.h> 15 #include <sal/core/alloc.h> 16 #include <sal/core/libc.h> 17 #include <sal/core/time.h> 18 #include <sal/types.h> 19 #include <shared/error.h> 20 #include <shared/bsl.h> 21 #include <soc/util.h> 22 #include <soc/drv.h> 23 24 #ifdef BCM_DNX_SUPPORT 25 #include <soc/dnx/dnx_err_recovery_manager_utils.h> 26 #endif 27 28 #define MAX_NAME_SIZE 30 29 30 /* 31 32 Note about termonology of objects and handlers in this file: 33 ------------------------------------------------ 34 periodic_event_handler_t (which is void*) and periodic_event_object_t* 35 are the same entiry. 36 */ 37 38 /* PE internal description - shouldn't be exposed to periodic event user*/ 39 typedef struct periodic_event_object_s { 40 int unit; /* unit # */ 41 char name[MAX_NAME_SIZE]; /* name given by the user (see periodic_event.h)*/ 42 periodic_event_f callback; /* callback provided by the user (see periodic_event.h)*/ 43 void* user_data; /* user data pointer provided by the user (see periodic_event.h)*/ 44 sal_sem_t sema; /* Semaphore handler for periodic_event_thread usage */ 45 int interval; /* interval provided by the user (see periodic_event.h)*/ 46 int quit; /* indication from main thread whether thread should be exit */ 47 int thread_priority; /* thread priority provided by the user (see periodic_event.h)*/ 48 sal_thread_t tid; /* created thread ID */ 49 int error_threshold; /* error count threshold provided by the user (see periodic_event.h)*/ 50 uint32 bsl_module; /* BSL module for logging messages */ 51 } periodic_event_object_t; 52 53 54 /** This function will be registered to sal_thread_create 55 * The function operation: 56 * 1. Iterate unless inidcate to quit 57 * 1.a. Wait on a semaphore (until triggered or until 58 * timeout provided by the user is passing 59 * 1.b. Execute user callback. 60 * 1.c. Check call back error 61 * if error inc error count, otherwise zero error 62 * count. 63 * 2.d. If error_count > 0 quit the loop. 64 * 2. When thread is about to exit: 65 * 2.a indicate active is false by changing tid to invalid. 66 * 2.b. do sal_thread_exit. 67 */ 68 static void periodic_event_thread(periodic_event_object_t* obj) 69 { 70 int rv, err_count = 0; 71 72 LOG_VERBOSE(obj->bsl_module, (BSL_META("PE: Thraed is starting.\n"))); 73 74 #ifdef BCM_DNX_SUPPORT 75 if(SOC_IS_DNX(obj->unit)) 76 { 77 DNX_ERR_RECOVERY_UTILS_EXCLUDED_THREAD_ADD(obj->unit); 78 } 79 #endif 80 81 /* iterate until indicate to exit */ 82 while (!obj->quit) { 83 84 /* wait for trigger or timeout */ 85 (void)sal_sem_take(obj->sema, obj->interval); 86 87 /* break out if we're told to quit */ 88 if (obj->quit == 1) { 89 break; 90 } 91 92 /* run callback */ 93 rv = obj->callback(obj->unit, obj->user_data); 94 95 /* update error count */ 96 if (_SHR_E_FAILURE(rv)) { 97 LOG_WARN(obj->bsl_module, (BSL_META("PE: Callback failure occured on the %d time in a raw. rv: %d\n"), err_count, rv)); 98 err_count++; 99 } else { 100 err_count = 0; 101 } 102 103 /* Exist if passed error count threshold. 104 -1 means ignore error counting */ 105 if (err_count >= obj->error_threshold && obj->error_threshold != -1) { 106 LOG_ERROR(obj->bsl_module, (BSL_META("PE: Error threshold acceded. Thread will exit.\n"))); 107 break; 108 } 109 110 } 111 112 LOG_VERBOSE(obj->bsl_module, (BSL_META("PE: Thraed is stopping.\n"))); 113 114 #ifdef BCM_DNX_SUPPORT 115 if(SOC_IS_DNX(obj->unit)) 116 { 117 DNX_ERR_RECOVERY_UTILS_EXCLUDED_THREAD_REMOVE(obj->unit); 118 } 119 #endif 120 121 /* indicate thread isn't active anymore */ 122 obj->tid = SAL_THREAD_ERROR; 123 124 /* exit thread */ 125 sal_thread_exit(0); 126 } 127 128 /** see .h file */ 129 void periodic_event_config_t_init(periodic_event_config_t* config) 130 { 131 sal_memset(config, 0, sizeof(periodic_event_config_t)); 132 133 /* set defaults */ 134 config->thread_priority = 50; 135 config->error_threshold = -1; 136 config->start_operation = 1; 137 config->bsl_module = -1; 138 } 139 140 /** see .h file */ 141 int periodic_event_create(int unit, const periodic_event_config_t* config, periodic_event_handler_t* h) 142 { 143 int rv; 144 periodic_event_object_t *obj; 145 146 *h = NULL; 147 148 /* check mandatory configs*/ 149 if (config->bsl_module == -1) { 150 return _SHR_E_CONFIG; 151 } 152 153 if (config->name[0] == 0) { 154 LOG_ERROR(config->bsl_module, (BSL_META("PE: Name is mandatory\n"))); 155 return _SHR_E_CONFIG; 156 } 157 158 159 if (config->interval <= 0 && config->interval != sal_sem_FOREVER) { 160 LOG_ERROR(config->bsl_module, (BSL_META("PE[%s]: Interval must be > 0\n"), config->name)); 161 return _SHR_E_CONFIG; 162 } 163 164 if (config->callback == NULL) { 165 LOG_ERROR(config->bsl_module, (BSL_META("PE[%s]: Callback must be assigned \n"), config->name)); 166 return _SHR_E_CONFIG; 167 } 168 169 /* allocate obj */ 170 obj = sal_alloc(sizeof(periodic_event_object_t), config->name); 171 if (obj == NULL) { 172 LOG_ERROR(config->bsl_module, (BSL_META("PE[%s]: Failed to allocate memory\n"), config->name)); 173 return _SHR_E_MEMORY; 174 } 175 176 /* initialize obj */ 177 sal_memset(obj, 0, sizeof(periodic_event_object_t)); 178 179 obj->unit = unit; 180 sal_strncpy(obj->name, config->name, MAX_NAME_SIZE-1); 181 obj->callback = config->callback; 182 obj->user_data = config->user_data; 183 obj->interval = config->interval; 184 obj->quit = 0; 185 obj->tid = SAL_THREAD_ERROR; 186 obj->thread_priority = config->thread_priority; 187 obj->error_threshold = config->error_threshold; 188 obj->bsl_module = config->bsl_module; 189 190 /* initialize semaphore */ 191 obj->sema = sal_sem_create(config->name, sal_sem_BINARY, 0); 192 if (obj->sema == NULL) { 193 LOG_ERROR(config->bsl_module, (BSL_META("PE[%s]: Failed to allocate semaphore\n"), config->name)); 194 sal_free(obj); 195 obj = NULL; 196 return _SHR_E_MEMORY; 197 } 198 199 /* init the thraed if requested */ 200 if (config->start_operation) { 201 rv = periodic_event_start((periodic_event_handler_t)obj); 202 if (_SHR_E_FAILURE(rv)) { 203 LOG_ERROR(config->bsl_module, (BSL_META("PE[%s]: Failed to start event\n"), config->name)); 204 sal_sem_destroy(obj->sema); 205 sal_free(obj); 206 return rv; 207 } 208 } 209 210 *h = (periodic_event_handler_t)obj; 211 212 return _SHR_E_NONE; 213 214 } 215 216 217 /** see .h file */ 218 int periodic_event_destroy(periodic_event_handler_t* h) 219 { 220 int rv; 221 222 if (h != NULL) { 223 224 periodic_event_object_t *obj = (periodic_event_object_t *)(*h); 225 226 /* stop thread if still running.*/ 227 rv = periodic_event_stop(*h, 0); 228 if (rv != _SHR_E_NONE) { 229 LOG_ERROR(obj->bsl_module, (BSL_META("PE[%s]: Failed to stop the thread \n"), obj->name)); 230 return rv; 231 } 232 233 /* deallocate */ 234 if (obj != NULL) { 235 if (obj->sema != NULL) { 236 sal_sem_destroy(obj->sema); 237 } 238 239 sal_free(obj); 240 241 } 242 243 *h = NULL; 244 } 245 246 return _SHR_E_NONE; 247 248 } 249 250 /* this function return 1 if thread was created, 0 otherwise*/ 251 static int is_thread_created(periodic_event_handler_t h) 252 { 253 periodic_event_object_t *obj = (periodic_event_object_t*)h; 254 255 if(obj->tid == SAL_THREAD_ERROR) { 256 return 0; /* tid indicate thread was not created*/ 257 } else { 258 return 1; 259 } 260 } 261 262 /** see .h file */ 263 int periodic_event_is_active_get(periodic_event_handler_t h, int* is_active) 264 { 265 *is_active = is_thread_created(h); 266 267 return _SHR_E_NONE; 268 } 269 270 /** see .h file */ 271 int periodic_event_trigger(periodic_event_handler_t h) 272 { 273 periodic_event_object_t *obj = (periodic_event_object_t*)h; 274 275 /* if thread isn't running - return an error*/ 276 if (!is_thread_created(h)) { 277 LOG_ERROR(obj->bsl_module, (BSL_META("PE[%s]: Thread isn't running \n"), obj->name)); 278 return _SHR_E_DISABLED; 279 } 280 281 /* trigger event */ 282 (void)sal_sem_give(obj->sema); 283 284 return _SHR_E_NONE; 285 } 286 287 /** see .h file */ 288 int periodic_event_start(periodic_event_handler_t h) 289 { 290 periodic_event_object_t *obj = (periodic_event_object_t*)h; 291 292 /* if thread is already runnig - return an error*/ 293 if (is_thread_created(h)) { 294 LOG_ERROR(obj->bsl_module, (BSL_META("PE[%s]: Thread already created \n"), obj->name)); 295 return _SHR_E_EXISTS; 296 } 297 298 /* indicate we don't want to quit */ 299 obj->quit = 0; 300 301 /* create the trhead */ 302 obj->tid = sal_thread_create(obj->name, 303 SAL_THREAD_STKSZ, 304 obj->thread_priority, 305 (void (*)(void*))periodic_event_thread, 306 (void*)obj); 307 308 if(obj->tid == SAL_THREAD_ERROR){ 309 LOG_ERROR(obj->bsl_module, (BSL_META("PE[%s]: Failed to create a thread \n"), obj->name)); 310 return _SHR_E_MEMORY; 311 } 312 313 return _SHR_E_NONE; 314 } 315 316 /* 1 second */ 317 #define PERIODIC_EVENT_THREAD_EXIT_TIMEOUT (1*SECOND_USEC) 318 319 /** see .h file */ 320 int periodic_event_stop(periodic_event_handler_t h, int dont_wait) 321 { 322 int rv = _SHR_E_NONE; 323 soc_timeout_t to; 324 periodic_event_object_t *obj = (periodic_event_object_t*)h; 325 326 /* if already stopped - do nothing */ 327 if (!is_thread_created(h)) { 328 LOG_VERBOSE(obj->bsl_module, (BSL_META("PE[%s]: Thread isn't running. Nothing to do. \n"), obj->name)); 329 return _SHR_E_NONE; 330 } 331 332 /* indicate quit indication */ 333 obj->quit = 1; 334 335 /* if waiting for thread to exit is not required - get out */ 336 if(dont_wait) 337 { 338 LOG_VERBOSE(obj->bsl_module, (BSL_META("PE[%s]: Thread will stop next time it is scheduled.\n"), obj->name)); 339 return _SHR_E_NONE; 340 } 341 342 /* trigger event for quit marking to take place */ 343 _SHR_E_IF_ERROR_RETURN(periodic_event_trigger(h)); 344 345 /* wait for thread to exit */ 346 soc_timeout_init(&to, PERIODIC_EVENT_THREAD_EXIT_TIMEOUT, 100); 347 348 while (is_thread_created(h)) { 349 if (soc_timeout_check(&to)) { 350 LOG_WARN(obj->bsl_module, (BSL_META("PE[%s]: Failed to gracefully turn off thread. Will terminate it. \n"), obj->name)); 351 rv = _SHR_E_TIMEOUT; 352 break; 353 } 354 } 355 356 return rv; 357 } 358 359 /** see .h file */ 360 int periodic_event_interval_set(periodic_event_handler_t h, int interval) 361 { 362 int is_same_thread; 363 periodic_event_object_t *obj = (periodic_event_object_t*)h; 364 365 if (interval <= 0 && interval != sal_sem_FOREVER) { 366 LOG_ERROR(obj->bsl_module, (BSL_META("PE[%s]: Interval must be > 0\n"), obj->name)); 367 return _SHR_E_CONFIG; 368 } 369 370 /* update interval */ 371 obj->interval = interval; 372 373 /* trigger the event for new interval to take affect*/ 374 if (is_thread_created(h)) { 375 _SHR_E_IF_ERROR_RETURN(periodic_event_is_periodic_context_get(h, &is_same_thread)); 376 if (!is_same_thread) { 377 _SHR_E_IF_ERROR_RETURN(periodic_event_trigger(h)); 378 } 379 } 380 381 return _SHR_E_NONE; 382 } 383 384 /** see .h file */ 385 int periodic_event_interval_get(periodic_event_handler_t h, int* interval) 386 { 387 periodic_event_object_t *obj = (periodic_event_object_t*)h; 388 389 *interval = obj->interval; 390 391 return _SHR_E_NONE; 392 } 393 394 /** see .h file */ 395 int periodic_event_is_periodic_context_get(periodic_event_handler_t h, int* is_periodic_context) 396 { 397 periodic_event_object_t *obj = (periodic_event_object_t*)h; 398 399 *is_periodic_context = (obj->tid == sal_thread_self() ? 1 : 0); 400 401 return _SHR_E_NONE; 402 } 403 404 /** see .h file */ 405 int periodic_event_is_quit_signaled_get(periodic_event_handler_t h, int* is_quit_signaled) 406 { 407 408 periodic_event_object_t *obj = (periodic_event_object_t*)h; 409 410 *is_quit_signaled = obj->quit; 411 412 return _SHR_E_NONE; 413 414 } 415 416