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

dpc.c (12606B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File: 	dpc.c
      8  * Purpose: 	Deferred Procedure Call module
      9  *
     10  * sal_dpc_init() starts a thread to process deferred procedure calls.
     11  * sal_dpc_term() stops the thread.
     12  *
     13  * sal_dpc() enqueues a function that will be called by the thread ASAP.
     14  * sal_dpc_time() enqueues a function that will be called by the
     15  *		thread a specified amount of time in the future.
     16  *
     17  * DPC allows owner range in full uint32 range but keeps owner status for
     18  * owner between 0 and SAL_DPC_MAX_NUM_UNITS, which is ususally the unit ID
     19  * in a multi-chip environment. DPC will check the status of owners in this
     20  * range while executing DPC from them. The following functions are supported
     21  * for the owners in SAL_DPC_MAX_NUM_UNITS range.
     22  *
     23  * sal_dpc_enable() enable a specified owner to execute DPCs
     24  * sal_dpc_disable() submit a dpc to disable a specified owner to execute DPCs
     25  * sal_dpc_disable_and_wait() disable a specified owner to execute DPCs and
     26  *      wait until all reamining dpc's are executed for this owner
     27  *
     28  * When DPC user pass NULL for parameter owner, DPC will assign the owner
     29  * to be SAL_DPC_OWNER_NULL so we can avoid unexpected owner value
     30  */
     31 
     32 #include <assert.h>
     33 
     34 #include <sal/core/alloc.h>
     35 #include <sal/core/spl.h>
     36 #include <sal/core/sync.h>
     37 #include <sal/core/time.h>
     38 #include <sal/core/dpc.h>
     39 
     40 #define SAL_DPC_COUNT           256
     41 #define SAL_DPC_THREAD_PRIO     50
     42 #define SAL_DPC_MAX_NUM_UNITS   128 /* align with BCM_MAX_NUM_UNITS */
     43 
     44 /* DPC owner status flag define */
     45 #define SAL_DPC_STATUS_ACTIVE       (0x01)  /* Bit 0 */
     46 
     47 /* DPC internal macros */
     48 #define SAL_DPC_OWNER_IS_DEV_ID(x)  \
     49     (PTR_TO_INT(x) < SAL_DPC_MAX_NUM_UNITS)
     50 #define SAL_DPC_OWNER_IS_ACTIVE(x)  \
     51     (sal_dpc_owner_stat[PTR_TO_INT(x)] & SAL_DPC_STATUS_ACTIVE)
     52 
     53 typedef struct sal_dpc_s {
     54     struct sal_dpc_s	*sd_next;	/* Forward pointer */
     55     sal_usecs_t		sd_t;		/* Absolute time in usec */
     56     void		(*sd_f)(void *, void *, void *, void *, void *);
     57     void		*sd_owner;	/* First parameter passed to sd_f */
     58     void		*sd_p2;		/* Four more parameters passed */
     59     void		*sd_p3;
     60     void		*sd_p4;
     61     void		*sd_p5;
     62 } sal_dpc_t;
     63 
     64 static int		sal_dpc_count	= SAL_DPC_COUNT;
     65 static int		sal_dpc_prio	= SAL_DPC_THREAD_PRIO;
     66 static sal_sem_t	sal_dpc_sem	= NULL;	/* Semaphore to sleep on */
     67 static sal_dpc_t	*sal_dpc_free	= NULL;	/* Free callout structs */
     68 static sal_dpc_t	*sal_dpc_alloc	= NULL;	/* Original allocation */
     69 static sal_dpc_t	*sal_dpc_q	= NULL;	/* Pending callouts */
     70 static sal_spinlock_t	sal_dpc_lock = NULL;
     71 static volatile sal_thread_t	sal_dpc_threadid = SAL_THREAD_ERROR;
     72 static int sal_dpc_owner_stat[SAL_DPC_MAX_NUM_UNITS];
     73 
     74 /*
     75  * Function:
     76  * 	_sal_dpc_cleanup
     77  * Purpose:
     78  *	Free allocated DPC resources.
     79  * Parameters:
     80  *	None
     81  * Returns:
     82  *	Nothing.
     83  */
     84 
     85 STATIC void
     86 _sal_dpc_cleanup(void)
     87 {
     88     if (sal_dpc_sem != NULL) {
     89 	sal_sem_destroy(sal_dpc_sem);
     90 	sal_dpc_sem = NULL;
     91     }
     92 
     93     if (sal_dpc_alloc != NULL) {
     94 	sal_free(sal_dpc_alloc);
     95 	sal_dpc_alloc = NULL;
     96     }
     97 
     98     if (sal_dpc_lock != NULL) {
     99         sal_spinlock_destroy(sal_dpc_lock);
    100         sal_dpc_lock = NULL;
    101     }
    102 }
    103 
    104 /*
    105  * Function:
    106  * 	sal_dpc_thread
    107  * Purpose:
    108  *	Background deferred procedure call thread used as context for DPCs.
    109  * Parameters:
    110  *	arg (Ignored)
    111  * Returns:
    112  *	Does not return
    113  */
    114 
    115 STATIC void
    116 sal_dpc_thread(void *arg)
    117 {
    118     sal_dpc_t	*d;
    119 
    120     COMPILER_REFERENCE(arg);
    121 
    122     while (1) {
    123 	sal_usecs_t	cur_time;
    124 
    125 	/*
    126 	 * Wait until it is time to do work, based on the top of the
    127 	 * work queue.
    128 	 */
    129 
    130 	if (sal_dpc_q == NULL) {
    131 	    (void)sal_sem_take(sal_dpc_sem, sal_sem_FOREVER);
    132 	} else {
    133 	    int t = SAL_USECS_SUB(sal_dpc_q->sd_t, sal_time_usecs());
    134 	    if (t > 0) {
    135 		(void)sal_sem_take(sal_dpc_sem, t);
    136 	    }
    137 	}
    138 
    139 	/* Process all DPCs past due */
    140 
    141 	cur_time = sal_time_usecs();
    142 
    143 	sal_spinlock_lock(sal_dpc_lock);
    144 
    145 	while ((d = sal_dpc_q) != NULL &&
    146 	       SAL_USECS_SUB(d->sd_t, cur_time) <= 0) {
    147 	    sal_dpc_q = d->sd_next;
    148 	    sal_spinlock_unlock(sal_dpc_lock);
    149         if(SAL_DPC_OWNER_IS_DEV_ID(d->sd_owner)) {
    150             if (SAL_DPC_OWNER_IS_ACTIVE(d->sd_owner)) {
    151                 d->sd_f(d->sd_owner, d->sd_p2, d->sd_p3, d->sd_p4, d->sd_p5);
    152             }
    153         } else {
    154             d->sd_f(d->sd_owner, d->sd_p2, d->sd_p3, d->sd_p4, d->sd_p5);
    155         }
    156 	    sal_spinlock_lock(sal_dpc_lock);
    157 	    d->sd_next = sal_dpc_free;	/* Free queue entry */
    158 	    sal_dpc_free = d;
    159 	}
    160 
    161 	sal_spinlock_unlock(sal_dpc_lock);
    162     }
    163 }
    164 
    165 STATIC void
    166 _sal_dpc_thread_exit(void *owner, void *p2, void *p3, void *p4, void *p5)
    167 {
    168     COMPILER_REFERENCE(owner);
    169     COMPILER_REFERENCE(p2);
    170     COMPILER_REFERENCE(p3);
    171     COMPILER_REFERENCE(p4);
    172     COMPILER_REFERENCE(p5);
    173 
    174     sal_dpc_threadid = SAL_THREAD_ERROR;
    175     sal_thread_exit(0);
    176 }
    177 
    178 /*
    179  * Function:
    180  *  sal_dpc_term
    181  * Purpose:
    182  *  Terminate DPC support.
    183  * Parameters:
    184  *  None
    185  * Returns:
    186  *  Nothing
    187  */
    188 void
    189 sal_dpc_term(void)
    190 {
    191     if (sal_dpc_threadid != SAL_THREAD_ERROR) {
    192     sal_dpc(_sal_dpc_thread_exit, INT_TO_PTR(-1), 0, 0, 0, 0);
    193 
    194 	sal_usleep(100);	/* tiny sleep first */
    195 	while (sal_dpc_threadid != SAL_THREAD_ERROR) {
    196 	    sal_usleep(10000);
    197 	}
    198     }
    199 
    200     _sal_dpc_cleanup();
    201 
    202     sal_dpc_q = NULL;
    203 }
    204 
    205 /*
    206  * Function:
    207  * 	sal_dpc_init
    208  * Purpose:
    209  *	Initialize DPC support.
    210  * Parameters:
    211  *	None
    212  * Returns:
    213  *	0 on success, non-zero on failure
    214  * Notes:
    215  *	This must be called before other DPC routines can be used.
    216  *	It must not be called from interrupt context.
    217  */
    218 
    219 int
    220 sal_dpc_init(void)
    221 {
    222     int		i;
    223 
    224     if (sal_dpc_threadid != SAL_THREAD_ERROR) {
    225 	sal_dpc_term();
    226     }
    227 
    228     sal_dpc_sem = sal_sem_create("sal_dpc_sem", TRUE, 0);
    229     sal_dpc_alloc = sal_alloc(sizeof(sal_dpc_t) * sal_dpc_count, "sal_dpc");
    230     sal_dpc_lock = sal_spinlock_create("sal_dpc_lock");
    231 
    232     if (sal_dpc_sem == NULL ||
    233 	sal_dpc_alloc == NULL ||
    234 	sal_dpc_lock == NULL) {
    235         _sal_dpc_cleanup();
    236 	return -1;
    237     }
    238 
    239     /* Reset all owner status before thread starts */
    240     for (i = 0; i < SAL_DPC_MAX_NUM_UNITS; i++) {
    241         sal_dpc_owner_stat[i] = SAL_DPC_STATUS_ACTIVE;
    242     }
    243 
    244     sal_dpc_threadid =
    245 	sal_thread_create("bcmDPC",
    246 			  SAL_THREAD_STKSZ,
    247 			  sal_dpc_prio,
    248 			  sal_dpc_thread, 0);
    249 
    250     if (sal_dpc_threadid == SAL_THREAD_ERROR) {
    251         _sal_dpc_cleanup();
    252 	return -1;
    253     }
    254 
    255     sal_dpc_free = sal_dpc_alloc;
    256 
    257     for (i = 0; i < sal_dpc_count - 1; i++) {
    258 	sal_dpc_free[i].sd_next = &sal_dpc_free[i + 1];
    259     }
    260 
    261     sal_dpc_free[sal_dpc_count - 1].sd_next = NULL;
    262 
    263     return 0;
    264 }
    265 
    266 /*
    267  * Function:
    268  *	sal_dpc_config
    269  * Purpose:
    270  *	set configuration parameters and reinitialize dpc subsystem
    271  * Parameters:
    272  *	count	- number of entries (use default if <= 0)
    273  *	prio	- task priority (use defaults if <= 0)
    274  * Returns:
    275  *	0 on success, non-zero on failure
    276  */
    277 
    278 int
    279 sal_dpc_config(int count, int prio)
    280 {
    281     if (count <= 0) {
    282 	count = SAL_DPC_COUNT;
    283     }
    284 
    285     if (prio <= 0) {
    286 	prio = SAL_DPC_THREAD_PRIO;
    287     }
    288 
    289     if (count == sal_dpc_count &&
    290 	prio == sal_dpc_prio &&
    291 	sal_dpc_threadid != SAL_THREAD_ERROR) {
    292 	return 0;		/* already running with these parameters */
    293     }
    294 
    295     sal_dpc_count = count;
    296     sal_dpc_prio = prio;
    297     sal_dpc_term();
    298 
    299     return sal_dpc_init();
    300 }
    301 
    302 /*
    303  * Function:
    304  * 	sal_dpc_time
    305  * Purpose:
    306  *	Deferred procedure mechanism with timeout.
    307  * Parameters:
    308  *	usec - number of microseconds < 2^31 to wait before the function
    309  *		is called.  May be 0 to cause immediate callout.
    310  *	f  - function to call when timeout expires.
    311  *	owner, p2, p3, p4, p5 - parameters passed to callout function.
    312  * Returns:
    313  *	0 - Queued.
    314  *	-1 - failed.
    315  * Notes:
    316  *	May be called from interrupt context.
    317  */
    318 
    319 int
    320 sal_dpc_time(sal_usecs_t usec, sal_dpc_fn_t f,
    321 	     void *owner, void *p2, void *p3, void *p4, void *p5)
    322 {
    323     sal_dpc_t	*nt, *ct, *lt;		/* new/current/last timer */
    324     sal_usecs_t	now;
    325     int		need_to_give_sem;
    326 
    327     need_to_give_sem = 0;
    328 
    329     if (sal_dpc_threadid == SAL_THREAD_ERROR) {
    330         return -1;
    331     }
    332 
    333     now = sal_time_usecs();
    334 
    335     sal_spinlock_lock(sal_dpc_lock);
    336 
    337     if ((nt = sal_dpc_free) != NULL) {
    338 	sal_dpc_free = nt->sd_next;
    339 
    340 	nt->sd_t = SAL_USECS_ADD(now, usec);
    341 	nt->sd_f  = f;
    342 	nt->sd_owner = owner;
    343 	nt->sd_p2 = p2;
    344 	nt->sd_p3 = p3;
    345 	nt->sd_p4 = p4;
    346 	nt->sd_p5 = p5;
    347 	nt->sd_next = NULL;
    348 
    349 	/*
    350 	 * Find location in time queue and insert, note that this search and
    351 	 * insert results in multiple entries for the same time being called
    352 	 * in the order in which they were inserted.
    353 	 */
    354 	for (lt = NULL, ct = sal_dpc_q; ct; lt = ct, ct = ct->sd_next) {
    355 	    if (SAL_USECS_SUB(nt->sd_t, ct->sd_t) < 0) {
    356 		break;
    357 	    }
    358 	}
    359 
    360 	/*
    361 	 * lt is NULL if insert first entry, or points to entry after which
    362 	 * we should insert.
    363 	 */
    364 
    365 	if (lt) {
    366 	    nt->sd_next = lt->sd_next;
    367 	    lt->sd_next = nt;
    368 	} else {
    369 	    nt->sd_next = sal_dpc_q;
    370 	    sal_dpc_q	= nt;
    371 	    need_to_give_sem = 1;
    372 	}
    373     }
    374 
    375     sal_spinlock_unlock(sal_dpc_lock);
    376 
    377     if(need_to_give_sem) {
    378 	sal_sem_give(sal_dpc_sem);	/* Wake up, old sleep value stale */
    379     }
    380 
    381     return (nt != NULL ? 0 : -1);
    382 }
    383 
    384 /*
    385  * Function:
    386  * 	sal_dpc
    387  * Purpose:
    388  *	Deferred procedure mechanism with-out timeout.
    389  * Parameters:
    390  *	f - function to call when timeout expires.
    391  *	owner, p2, p3, p4, p5 - parameters passed to callout function.
    392  * Returns:
    393  *	0 - Queued.
    394  *	-1 - failed.
    395  * Notes:
    396  *	May be called from interrupt context.
    397  */
    398 
    399 int
    400 sal_dpc(sal_dpc_fn_t f, void *owner, void *p2, void *p3, void *p4, void *p5)
    401 {
    402     return (sal_dpc_time((sal_usecs_t) 0, f, owner, p2, p3, p4, p5));
    403 }
    404 
    405 /*
    406  * Function:
    407  *	sal_dpc_cancel
    408  *
    409  * Purpose:
    410  *	Cancel all DPCs belonging to a specified owner.
    411  *
    412  * Parameters:
    413  *	owner - first parameter to DPC calls
    414  *
    415  * Notes:
    416  *	Each DPC currently waiting on the queue whose first
    417  *	argument (owner) matches the specified value is removed
    418  *	from the queue and never executed.
    419  */
    420 void
    421 sal_dpc_cancel(void *owner)
    422 {
    423     sal_dpc_t	**dp, *d;
    424 
    425     sal_spinlock_lock(sal_dpc_lock);;
    426 
    427     dp = &sal_dpc_q;
    428 
    429     while ((d = *dp) != NULL) {
    430 	if (d->sd_owner == owner) {
    431 	    (*dp) = d->sd_next;
    432 	    d->sd_next = sal_dpc_free;
    433 	    sal_dpc_free = d;
    434 	} else {
    435 	    dp = &(*dp)->sd_next;
    436 	}
    437     }
    438 
    439     sal_spinlock_unlock(sal_dpc_lock);
    440 }
    441 
    442 /*
    443  * Function:
    444  *  sal_dpc_enable
    445  *
    446  * Purpose:
    447  *  Enable a specified owner to run DPCs
    448  *
    449  * Parameters:
    450  *  owner - first parameter to DPC calls
    451  * Returns:
    452  *	0  - succeed
    453  *	-1 - failed
    454  */
    455 int
    456 sal_dpc_enable(void *owner)
    457 {
    458     if(SAL_DPC_OWNER_IS_DEV_ID(owner)) {
    459         sal_dpc_owner_stat[PTR_TO_INT(owner)] |= SAL_DPC_STATUS_ACTIVE;
    460         return 0;
    461     }
    462     return -1;
    463 }
    464 
    465 void
    466 _sal_dpc_disable(void *owner, void *p2, void *p3, void *p4, void *p5)
    467 {
    468     if(SAL_DPC_OWNER_IS_DEV_ID(owner)) {
    469         sal_dpc_owner_stat[PTR_TO_INT(owner)] &= ~SAL_DPC_STATUS_ACTIVE;
    470     }
    471 }
    472 
    473 /*
    474  * Function:
    475  *  sal_dpc_cancel_and_disable
    476  *
    477  * Purpose:
    478  *  Cancel all DPCs belonging to a specified owner, and then
    479  * disable a specified owner to execute DPCs
    480  *
    481  * Parameters:
    482  *  owner - first parameter to DPC calls
    483  *
    484  * Notes:
    485  *  Each DPC currently waiting on the queue whose first
    486  *  argument (owner) matches the specified value is removed
    487  *  from the queue and never executed.
    488  */
    489 void
    490 sal_dpc_cancel_and_disable(void *owner)
    491 {
    492     sal_dpc_t   **dp, *d;
    493 
    494     sal_spinlock_lock(sal_dpc_lock);
    495 
    496     dp = &sal_dpc_q;
    497 
    498     while ((d = *dp) != NULL) {
    499         if (d->sd_owner == owner) {
    500             (*dp) = d->sd_next;
    501             d->sd_next = sal_dpc_free;
    502             sal_dpc_free = d;
    503         } else {
    504             dp = &(*dp)->sd_next;
    505         }
    506     }
    507     _sal_dpc_disable(owner, 0, 0, 0, 0);
    508 
    509     sal_spinlock_unlock(sal_dpc_lock);
    510 }
    511 
    512 /*
    513  * Function:
    514  *  sal_dpc_disable
    515  *
    516  * Purpose:
    517  *  Disable a specified owner to execute DPCs
    518  *
    519  * Parameters:
    520  *  owner - first parameter to DPC calls
    521  * Returns:
    522  *	0  - succeed
    523  *	-1 - failed
    524  */
    525 int
    526 sal_dpc_disable(void *owner)
    527 {
    528     if(SAL_DPC_OWNER_IS_DEV_ID(owner)) {
    529         return (sal_dpc(_sal_dpc_disable, owner, 0, 0, 0, 0));
    530     }
    531     return -1;
    532 }
    533 
    534 /*
    535  * Function:
    536  *  sal_dpc_disable_and_cancel
    537  *
    538  * Purpose:
    539  * disable a specified owner to execute DPCs,
    540  * and wait all DPCs belonging to owner to be done.
    541  *
    542  * Parameters:
    543  *  unit
    544  * Returns:
    545  *	0  - succeed
    546  *	-1 - failed
    547  */
    548 int
    549 sal_dpc_disable_and_wait(void *owner)
    550 {
    551     if(sal_dpc_disable(owner) == 0) {
    552         while (SAL_DPC_OWNER_IS_ACTIVE(owner)) {
    553             sal_usleep(100);
    554         }
    555         return 0;
    556     }
    557     return -1;
    558 }
    559