Wed Dec 26 20:08:22 2018 UTC ()
Use uint64_t for the unbound and per-cpu thread pool ref counts; they're
always manipulated under a lock.  Rather than bother returning EBUSY,
just assert that the ref count never overlows (if it ever does, you have
bigger problems).


(thorpej)
diff -r1.4 -r1.5 src/sys/kern/kern_threadpool.c

cvs diff -r1.4 -r1.5 src/sys/kern/kern_threadpool.c (expand / switch to unified diff)

--- src/sys/kern/kern_threadpool.c 2018/12/26 18:54:19 1.4
+++ src/sys/kern/kern_threadpool.c 2018/12/26 20:08:22 1.5
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: kern_threadpool.c,v 1.4 2018/12/26 18:54:19 thorpej Exp $ */ 1/* $NetBSD: kern_threadpool.c,v 1.5 2018/12/26 20:08:22 thorpej Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2014, 2018 The NetBSD Foundation, Inc. 4 * Copyright (c) 2014, 2018 The NetBSD Foundation, Inc.
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * This code is derived from software contributed to The NetBSD Foundation 7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell and Jason R. Thorpe. 8 * by Taylor R. Campbell and Jason R. Thorpe.
9 * 9 *
10 * Redistribution and use in source and binary forms, with or without 10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions 11 * modification, are permitted provided that the following conditions
12 * are met: 12 * are met:
13 * 1. Redistributions of source code must retain the above copyright 13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer. 14 * notice, this list of conditions and the following disclaimer.
@@ -71,27 +71,27 @@ @@ -71,27 +71,27 @@
71 * | | <running (n+1)b> | | 71 * | | <running (n+1)b> | |
72 * | +------------------+ | 72 * | +------------------+ |
73 * +--------------------------------------------------------+ 73 * +--------------------------------------------------------+
74 * 74 *
75 * XXX Why one overseer per CPU? I did that originally to avoid 75 * XXX Why one overseer per CPU? I did that originally to avoid
76 * touching remote CPUs' memory when scheduling a job, but that still 76 * touching remote CPUs' memory when scheduling a job, but that still
77 * requires interprocessor synchronization. Perhaps we could get by 77 * requires interprocessor synchronization. Perhaps we could get by
78 * with a single overseer thread, at the expense of another pointer in 78 * with a single overseer thread, at the expense of another pointer in
79 * struct threadpool_job to identify the CPU on which it must run 79 * struct threadpool_job to identify the CPU on which it must run
80 * in order for the overseer to schedule it correctly. 80 * in order for the overseer to schedule it correctly.
81 */ 81 */
82 82
83#include <sys/cdefs.h> 83#include <sys/cdefs.h>
84__KERNEL_RCSID(0, "$NetBSD: kern_threadpool.c,v 1.4 2018/12/26 18:54:19 thorpej Exp $"); 84__KERNEL_RCSID(0, "$NetBSD: kern_threadpool.c,v 1.5 2018/12/26 20:08:22 thorpej Exp $");
85 85
86#include <sys/types.h> 86#include <sys/types.h>
87#include <sys/param.h> 87#include <sys/param.h>
88#include <sys/atomic.h> 88#include <sys/atomic.h>
89#include <sys/condvar.h> 89#include <sys/condvar.h>
90#include <sys/cpu.h> 90#include <sys/cpu.h>
91#include <sys/kernel.h> 91#include <sys/kernel.h>
92#include <sys/kmem.h> 92#include <sys/kmem.h>
93#include <sys/kthread.h> 93#include <sys/kthread.h>
94#include <sys/mutex.h> 94#include <sys/mutex.h>
95#include <sys/once.h> 95#include <sys/once.h>
96#include <sys/percpu.h> 96#include <sys/percpu.h>
97#include <sys/pool.h> 97#include <sys/pool.h>
@@ -151,27 +151,27 @@ static void threadpool_thread(void *) __ @@ -151,27 +151,27 @@ static void threadpool_thread(void *) __
151static pool_cache_t threadpool_thread_pc __read_mostly; 151static pool_cache_t threadpool_thread_pc __read_mostly;
152 152
153static kmutex_t threadpools_lock __cacheline_aligned; 153static kmutex_t threadpools_lock __cacheline_aligned;
154 154
155 /* Idle out threads after 30 seconds */ 155 /* Idle out threads after 30 seconds */
156#define THREADPOOL_IDLE_TICKS mstohz(30 * 1000) 156#define THREADPOOL_IDLE_TICKS mstohz(30 * 1000)
157 157
158struct threadpool_unbound { 158struct threadpool_unbound {
159 /* must be first; see threadpool_create() */ 159 /* must be first; see threadpool_create() */
160 struct threadpool tpu_pool; 160 struct threadpool tpu_pool;
161 161
162 /* protected by threadpools_lock */ 162 /* protected by threadpools_lock */
163 LIST_ENTRY(threadpool_unbound) tpu_link; 163 LIST_ENTRY(threadpool_unbound) tpu_link;
164 unsigned int tpu_refcnt; 164 uint64_t tpu_refcnt;
165}; 165};
166 166
167static LIST_HEAD(, threadpool_unbound) unbound_threadpools; 167static LIST_HEAD(, threadpool_unbound) unbound_threadpools;
168 168
169static struct threadpool_unbound * 169static struct threadpool_unbound *
170threadpool_lookup_unbound(pri_t pri) 170threadpool_lookup_unbound(pri_t pri)
171{ 171{
172 struct threadpool_unbound *tpu; 172 struct threadpool_unbound *tpu;
173 173
174 LIST_FOREACH(tpu, &unbound_threadpools, tpu_link) { 174 LIST_FOREACH(tpu, &unbound_threadpools, tpu_link) {
175 if (tpu->tpu_pool.tp_pri == pri) 175 if (tpu->tpu_pool.tp_pri == pri)
176 return tpu; 176 return tpu;
177 } 177 }
@@ -188,27 +188,27 @@ threadpool_insert_unbound(struct threadp @@ -188,27 +188,27 @@ threadpool_insert_unbound(struct threadp
188static void 188static void
189threadpool_remove_unbound(struct threadpool_unbound *tpu) 189threadpool_remove_unbound(struct threadpool_unbound *tpu)
190{ 190{
191 KASSERT(threadpool_lookup_unbound(tpu->tpu_pool.tp_pri) == tpu); 191 KASSERT(threadpool_lookup_unbound(tpu->tpu_pool.tp_pri) == tpu);
192 LIST_REMOVE(tpu, tpu_link); 192 LIST_REMOVE(tpu, tpu_link);
193} 193}
194 194
195struct threadpool_percpu { 195struct threadpool_percpu {
196 percpu_t * tpp_percpu; 196 percpu_t * tpp_percpu;
197 pri_t tpp_pri; 197 pri_t tpp_pri;
198 198
199 /* protected by threadpools_lock */ 199 /* protected by threadpools_lock */
200 LIST_ENTRY(threadpool_percpu) tpp_link; 200 LIST_ENTRY(threadpool_percpu) tpp_link;
201 unsigned int tpp_refcnt; 201 uint64_t tpp_refcnt;
202}; 202};
203 203
204static LIST_HEAD(, threadpool_percpu) percpu_threadpools; 204static LIST_HEAD(, threadpool_percpu) percpu_threadpools;
205 205
206static struct threadpool_percpu * 206static struct threadpool_percpu *
207threadpool_lookup_percpu(pri_t pri) 207threadpool_lookup_percpu(pri_t pri)
208{ 208{
209 struct threadpool_percpu *tpp; 209 struct threadpool_percpu *tpp;
210 210
211 LIST_FOREACH(tpp, &percpu_threadpools, tpp_link) { 211 LIST_FOREACH(tpp, &percpu_threadpools, tpp_link) {
212 if (tpp->tpp_pri == pri) 212 if (tpp->tpp_pri == pri)
213 return tpp; 213 return tpp;
214 } 214 }
@@ -418,33 +418,28 @@ threadpool_get(struct threadpool **poolp @@ -418,33 +418,28 @@ threadpool_get(struct threadpool **poolp
418 tmp = container_of(new_pool, struct threadpool_unbound, 418 tmp = container_of(new_pool, struct threadpool_unbound,
419 tpu_pool); 419 tpu_pool);
420 mutex_enter(&threadpools_lock); 420 mutex_enter(&threadpools_lock);
421 tpu = threadpool_lookup_unbound(pri); 421 tpu = threadpool_lookup_unbound(pri);
422 if (tpu == NULL) { 422 if (tpu == NULL) {
423 TP_LOG(("%s: Won the creation race for pri=%d.\n", 423 TP_LOG(("%s: Won the creation race for pri=%d.\n",
424 __func__, (int)pri)); 424 __func__, (int)pri));
425 tpu = tmp; 425 tpu = tmp;
426 tmp = NULL; 426 tmp = NULL;
427 threadpool_insert_unbound(tpu); 427 threadpool_insert_unbound(tpu);
428 } 428 }
429 } 429 }
430 KASSERT(tpu != NULL); 430 KASSERT(tpu != NULL);
431 if (tpu->tpu_refcnt == UINT_MAX) { 
432 mutex_exit(&threadpools_lock); 
433 if (tmp != NULL) 
434 threadpool_destroy(&tmp->tpu_pool, sizeof(*tpu)); 
435 return EBUSY; 
436 } 
437 tpu->tpu_refcnt++; 431 tpu->tpu_refcnt++;
 432 KASSERT(tpu->tpu_refcnt != 0);
438 mutex_exit(&threadpools_lock); 433 mutex_exit(&threadpools_lock);
439 434
440 if (tmp != NULL) 435 if (tmp != NULL)
441 threadpool_destroy((struct threadpool *)tmp, sizeof(*tpu)); 436 threadpool_destroy((struct threadpool *)tmp, sizeof(*tpu));
442 KASSERT(tpu != NULL); 437 KASSERT(tpu != NULL);
443 *poolp = &tpu->tpu_pool; 438 *poolp = &tpu->tpu_pool;
444 return 0; 439 return 0;
445} 440}
446 441
447void 442void
448threadpool_put(struct threadpool *pool, pri_t pri) 443threadpool_put(struct threadpool *pool, pri_t pri)
449{ 444{
450 struct threadpool_unbound *tpu = 445 struct threadpool_unbound *tpu =
@@ -453,28 +448,29 @@ threadpool_put(struct threadpool *pool,  @@ -453,28 +448,29 @@ threadpool_put(struct threadpool *pool,
453 THREADPOOL_INIT(); 448 THREADPOOL_INIT();
454 449
455 ASSERT_SLEEPABLE(); 450 ASSERT_SLEEPABLE();
456 451
457 KASSERT(threadpool_pri_is_valid(pri)); 452 KASSERT(threadpool_pri_is_valid(pri));
458 453
459 mutex_enter(&threadpools_lock); 454 mutex_enter(&threadpools_lock);
460 KASSERT(tpu == threadpool_lookup_unbound(pri)); 455 KASSERT(tpu == threadpool_lookup_unbound(pri));
461 KASSERT(0 < tpu->tpu_refcnt); 456 KASSERT(0 < tpu->tpu_refcnt);
462 if (--tpu->tpu_refcnt == 0) { 457 if (--tpu->tpu_refcnt == 0) {
463 TP_LOG(("%s: Last reference for pri=%d, destroying pool.\n", 458 TP_LOG(("%s: Last reference for pri=%d, destroying pool.\n",
464 __func__, (int)pri)); 459 __func__, (int)pri));
465 threadpool_remove_unbound(tpu); 460 threadpool_remove_unbound(tpu);
466 } else 461 } else {
467 tpu = NULL; 462 tpu = NULL;
 463 }
468 mutex_exit(&threadpools_lock); 464 mutex_exit(&threadpools_lock);
469 465
470 if (tpu) 466 if (tpu)
471 threadpool_destroy(pool, sizeof(*tpu)); 467 threadpool_destroy(pool, sizeof(*tpu));
472} 468}
473 469
474/* Per-CPU thread pools */ 470/* Per-CPU thread pools */
475 471
476int 472int
477threadpool_percpu_get(struct threadpool_percpu **pool_percpup, pri_t pri) 473threadpool_percpu_get(struct threadpool_percpu **pool_percpup, pri_t pri)
478{ 474{
479 struct threadpool_percpu *pool_percpu, *tmp = NULL; 475 struct threadpool_percpu *pool_percpu, *tmp = NULL;
480 int error; 476 int error;
@@ -497,61 +493,57 @@ threadpool_percpu_get(struct threadpool_ @@ -497,61 +493,57 @@ threadpool_percpu_get(struct threadpool_
497 return error; 493 return error;
498 KASSERT(tmp != NULL); 494 KASSERT(tmp != NULL);
499 mutex_enter(&threadpools_lock); 495 mutex_enter(&threadpools_lock);
500 pool_percpu = threadpool_lookup_percpu(pri); 496 pool_percpu = threadpool_lookup_percpu(pri);
501 if (pool_percpu == NULL) { 497 if (pool_percpu == NULL) {
502 TP_LOG(("%s: Won the creation race for pri=%d.\n", 498 TP_LOG(("%s: Won the creation race for pri=%d.\n",
503 __func__, (int)pri)); 499 __func__, (int)pri));
504 pool_percpu = tmp; 500 pool_percpu = tmp;
505 tmp = NULL; 501 tmp = NULL;
506 threadpool_insert_percpu(pool_percpu); 502 threadpool_insert_percpu(pool_percpu);
507 } 503 }
508 } 504 }
509 KASSERT(pool_percpu != NULL); 505 KASSERT(pool_percpu != NULL);
510 if (pool_percpu->tpp_refcnt == UINT_MAX) { 
511 mutex_exit(&threadpools_lock); 
512 if (tmp != NULL) 
513 threadpool_percpu_destroy(tmp); 
514 return EBUSY; 
515 } 
516 pool_percpu->tpp_refcnt++; 506 pool_percpu->tpp_refcnt++;
 507 KASSERT(pool_percpu->tpp_refcnt != 0);
517 mutex_exit(&threadpools_lock); 508 mutex_exit(&threadpools_lock);
518 509
519 if (tmp != NULL) 510 if (tmp != NULL)
520 threadpool_percpu_destroy(tmp); 511 threadpool_percpu_destroy(tmp);
521 KASSERT(pool_percpu != NULL); 512 KASSERT(pool_percpu != NULL);
522 *pool_percpup = pool_percpu; 513 *pool_percpup = pool_percpu;
523 return 0; 514 return 0;
524} 515}
525 516
526void 517void
527threadpool_percpu_put(struct threadpool_percpu *pool_percpu, pri_t pri) 518threadpool_percpu_put(struct threadpool_percpu *pool_percpu, pri_t pri)
528{ 519{
529 520
530 THREADPOOL_INIT(); 521 THREADPOOL_INIT();
531 522
532 ASSERT_SLEEPABLE(); 523 ASSERT_SLEEPABLE();
533 524
534 KASSERT(threadpool_pri_is_valid(pri)); 525 KASSERT(threadpool_pri_is_valid(pri));
535 526
536 mutex_enter(&threadpools_lock); 527 mutex_enter(&threadpools_lock);
537 KASSERT(pool_percpu == threadpool_lookup_percpu(pri)); 528 KASSERT(pool_percpu == threadpool_lookup_percpu(pri));
538 KASSERT(0 < pool_percpu->tpp_refcnt); 529 KASSERT(0 < pool_percpu->tpp_refcnt);
539 if (--pool_percpu->tpp_refcnt == 0) { 530 if (--pool_percpu->tpp_refcnt == 0) {
540 TP_LOG(("%s: Last reference for pri=%d, destroying pool.\n", 531 TP_LOG(("%s: Last reference for pri=%d, destroying pool.\n",
541 __func__, (int)pri)); 532 __func__, (int)pri));
542 threadpool_remove_percpu(pool_percpu); 533 threadpool_remove_percpu(pool_percpu);
543 } else 534 } else {
544 pool_percpu = NULL; 535 pool_percpu = NULL;
 536 }
545 mutex_exit(&threadpools_lock); 537 mutex_exit(&threadpools_lock);
546 538
547 if (pool_percpu) 539 if (pool_percpu)
548 threadpool_percpu_destroy(pool_percpu); 540 threadpool_percpu_destroy(pool_percpu);
549} 541}
550 542
551struct threadpool * 543struct threadpool *
552threadpool_percpu_ref(struct threadpool_percpu *pool_percpu) 544threadpool_percpu_ref(struct threadpool_percpu *pool_percpu)
553{ 545{
554 struct threadpool **poolp, *pool; 546 struct threadpool **poolp, *pool;
555 547
556 poolp = percpu_getref(pool_percpu->tpp_percpu); 548 poolp = percpu_getref(pool_percpu->tpp_percpu);
557 pool = *poolp; 549 pool = *poolp;