openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

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