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