diff options
author | Patrick Mooney <pmooney@pfmooney.com> | 2016-11-09 22:09:53 +0000 |
---|---|---|
committer | Jason King <jason.king@joyent.com> | 2020-06-12 10:09:53 -0500 |
commit | 440a8a36792bdf9ef51639066aab0b7771ffcab8 (patch) | |
tree | ea5baee4f4fae2dac673fad600c6e88840336d5b /usr/src/uts/common/os/timer.c | |
parent | 926d645fe2416b8ee611fc8ee4e28b7c7f9744dd (diff) | |
download | illumos-gate-440a8a36792bdf9ef51639066aab0b7771ffcab8.tar.gz |
12789 increase timers allowed per-process
Reviewed by: Ryan Zezeski <rpz@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: John Levon <john.levon@joyent.com>
Reviewed by: Dan McDonald <danmcd@joyent.com>
Approved by: Robert Mustacchi <rm@fingolfin.org>
Diffstat (limited to 'usr/src/uts/common/os/timer.c')
-rw-r--r-- | usr/src/uts/common/os/timer.c | 189 |
1 files changed, 140 insertions, 49 deletions
diff --git a/usr/src/uts/common/os/timer.c b/usr/src/uts/common/os/timer.c index c8d3da26e2..7d061d878b 100644 --- a/usr/src/uts/common/os/timer.c +++ b/usr/src/uts/common/os/timer.c @@ -25,11 +25,12 @@ */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright 2020 Joyent, Inc. */ #include <sys/timer.h> #include <sys/systm.h> +#include <sys/sysmacros.h> #include <sys/param.h> #include <sys/kmem.h> #include <sys/debug.h> @@ -123,6 +124,7 @@ timer_delete_locked(proc_t *p, timer_t tid, itimer_t *it) timer_lock(p, it); } + ASSERT(p->p_itimer_sz > tid); ASSERT(p->p_itimer[tid] == it); p->p_itimer[tid] = NULL; @@ -201,12 +203,14 @@ timer_grab(proc_t *p, timer_t tid) { itimer_t **itp, *it; - if (tid >= timer_max || tid < 0) + if (tid < 0) { return (NULL); + } mutex_enter(&p->p_lock); - if ((itp = p->p_itimer) == NULL || (it = itp[tid]) == NULL) { + if ((itp = p->p_itimer) == NULL || tid >= p->p_itimer_sz || + (it = itp[tid]) == NULL) { mutex_exit(&p->p_lock); return (NULL); } @@ -258,6 +262,13 @@ clock_timer_init() { clock_timer_cache = kmem_cache_create("timer_cache", sizeof (itimer_t), 0, NULL, NULL, NULL, NULL, NULL, 0); + + /* + * Push the timer_max limit up to at least 4 * NCPU. Due to the way + * NCPU is defined, proper initialization of the timer limit is + * performed at runtime. + */ + timer_max = MAX(NCPU * 4, timer_max); } void @@ -466,13 +477,116 @@ timer_fire(itimer_t *it) mutex_exit(&p->p_lock); } +/* + * Find an unused (i.e. NULL) entry in p->p_itimer and set *id to the + * index of the unused entry, growing p->p_itimer as necessary (up to timer_max + * entries). Returns B_TRUE (with *id set) on success, B_FALSE on failure + * (e.g. the process already has the maximum number of allowed timers + * allocated). + */ +static boolean_t +timer_get_id(proc_t *p, timer_t *id) +{ + itimer_t **itp = NULL, **itp_new; + uint_t target_sz; + uint_t i; + + ASSERT(MUTEX_HELD(&p->p_lock)); + + if (p->p_itimer == NULL) { + /* + * No timers have been allocated for this process, allocate + * the initial array. + */ + ASSERT0(p->p_itimer_sz); + target_sz = _TIMER_ALLOC_INIT; + + mutex_exit(&p->p_lock); + itp_new = kmem_zalloc(target_sz * sizeof (itimer_t *), + KM_SLEEP); + mutex_enter(&p->p_lock); + + if (p->p_itimer == NULL) { + /* + * As long as no other thread beat us to allocating + * the initial p_itimer array, use what we allocated. + * Since we just allocated it, we know slot 0 is + * free. + */ + p->p_itimer = itp_new; + p->p_itimer_sz = target_sz; + i = 0; + goto done; + } + + /* + * Another thread beat us to allocating the initial array. + * Proceed to searching for an empty slot and growing the + * array if needed. + */ + kmem_free(itp_new, target_sz * sizeof (itimer_t *)); + } + +retry: + /* Use the first empty slot (if any exist) */ + for (i = 0; i < p->p_itimer_sz; i++) { + if (p->p_itimer[i] == NULL) { + goto done; + } + } + + /* No empty slots, try to grow p->p_itimer and retry */ + target_sz = p->p_itimer_sz * 2; + if (target_sz > timer_max || target_sz > INT_MAX || + target_sz < p->p_itimer_sz) { + /* Protect against exceeding the max or overflow */ + return (B_FALSE); + } + + mutex_exit(&p->p_lock); + itp_new = kmem_zalloc(target_sz * sizeof (itimer_t *), KM_SLEEP); + mutex_enter(&p->p_lock); + + if (target_sz <= p->p_itimer_sz) { + /* + * A racing thread performed the resize while we were + * waiting outside p_lock. Discard our now-useless + * allocation and retry. + */ + kmem_free(itp_new, target_sz * sizeof (itimer_t *)); + goto retry; + } + + ASSERT3P(p->p_itimer, !=, NULL); + bcopy(p->p_itimer, itp_new, p->p_itimer_sz * sizeof (itimer_t *)); + kmem_free(p->p_itimer, p->p_itimer_sz * sizeof (itimer_t *)); + + /* + * Short circuit to use the first free entry in the new allocation. + * It's possible that other lower-indexed timers were freed while + * p_lock was dropped, but skipping over them is not harmful at all. + * In the common case, we skip the need to walk over an array filled + * with timers before arriving at the slot we know is fresh from the + * allocation. + */ + i = p->p_itimer_sz; + + p->p_itimer = itp_new; + p->p_itimer_sz = target_sz; + +done: + ASSERT3U(i, <=, INT_MAX); + *id = (timer_t)i; + return (B_TRUE); +} + int timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) { struct sigevent ev; proc_t *p = curproc; clock_backend_t *backend; - itimer_t *it, **itp; + itimer_t *it; sigqueue_t *sigq; cred_t *cr = CRED(); int error = 0; @@ -547,51 +661,27 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) } /* - * We'll allocate our timer and sigqueue now, before we grab p_lock. - * If we can't find an empty slot, we'll free them before returning. + * We'll allocate our sigqueue now, before we grab p_lock. + * If we can't find an empty slot, we'll free it before returning. */ - it = kmem_cache_alloc(clock_timer_cache, KM_SLEEP); - bzero(it, sizeof (itimer_t)); - mutex_init(&it->it_mutex, NULL, MUTEX_DEFAULT, NULL); sigq = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP); - mutex_enter(&p->p_lock); - /* - * If this is this process' first timer, we need to attempt to allocate - * an array of timerstr_t pointers. We drop p_lock to perform the - * allocation; if we return to discover that p_itimer is non-NULL, - * we will free our allocation and drive on. + * Allocate a timer and choose a slot for it. */ - if ((itp = p->p_itimer) == NULL) { - mutex_exit(&p->p_lock); - itp = kmem_zalloc(timer_max * sizeof (itimer_t *), KM_SLEEP); - mutex_enter(&p->p_lock); - - if (p->p_itimer == NULL) - p->p_itimer = itp; - else { - kmem_free(itp, timer_max * sizeof (itimer_t *)); - itp = p->p_itimer; - } - } - - for (i = 0; i < timer_max && itp[i] != NULL; i++) - continue; + it = kmem_cache_alloc(clock_timer_cache, KM_SLEEP); + bzero(it, sizeof (*it)); + mutex_init(&it->it_mutex, NULL, MUTEX_DEFAULT, NULL); - if (i == timer_max) { - /* - * We couldn't find a slot. Drop p_lock, free the preallocated - * timer and sigqueue, and return an error. - */ + mutex_enter(&p->p_lock); + if (!timer_get_id(p, &i)) { mutex_exit(&p->p_lock); kmem_cache_free(clock_timer_cache, it); kmem_free(sigq, sizeof (sigqueue_t)); - return (set_errno(EAGAIN)); } - ASSERT(i < timer_max && itp[i] == NULL); + ASSERT(i < p->p_itimer_sz && p->p_itimer[i] == NULL); /* * If we develop other notification mechanisms, this will need @@ -613,8 +703,6 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) it->it_sigq = sigq; it->it_backend = backend; it->it_lock = ITLK_LOCKED; - itp[i] = it; - if (ev.sigev_notify == SIGEV_THREAD || ev.sigev_notify == SIGEV_PORT) { @@ -645,7 +733,6 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) (port_source_t **)&it->it_portsrc, timer_close_port, (void *)it, NULL); if (error) { - itp[i] = NULL; /* clear slot */ mutex_exit(&p->p_lock); kmem_cache_free(clock_timer_cache, it); kmem_free(sigq, sizeof (sigqueue_t)); @@ -658,7 +745,6 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) if (error) { (void) port_dissociate_ksource(port, PORT_SOURCE_TIMER, (port_source_t *)it->it_portsrc); - itp[i] = NULL; /* clear slot */ mutex_exit(&p->p_lock); kmem_cache_free(clock_timer_cache, it); kmem_free(sigq, sizeof (sigqueue_t)); @@ -675,6 +761,8 @@ timer_create(clockid_t clock, struct sigevent *evp, timer_t *tid) it->it_flags |= IT_SIGNAL; } + /* Populate the slot now that the timer is prepped. */ + p->p_itimer[i] = it; mutex_exit(&p->p_lock); /* @@ -832,7 +920,7 @@ timer_getoverrun(timer_t tid) void timer_lwpexit(void) { - timer_t i; + uint_t i; proc_t *p = curproc; klwp_t *lwp = ttolwp(curthread); itimer_t *it, **itp; @@ -842,7 +930,7 @@ timer_lwpexit(void) if ((itp = p->p_itimer) == NULL) return; - for (i = 0; i < timer_max; i++) { + for (i = 0; i < p->p_itimer_sz; i++) { if ((it = itp[i]) == NULL) continue; @@ -876,7 +964,7 @@ timer_lwpexit(void) void timer_lwpbind() { - timer_t i; + uint_t i; proc_t *p = curproc; klwp_t *lwp = ttolwp(curthread); itimer_t *it, **itp; @@ -886,7 +974,7 @@ timer_lwpbind() if ((itp = p->p_itimer) == NULL) return; - for (i = 0; i < timer_max; i++) { + for (i = 0; i < p->p_itimer_sz; i++) { if ((it = itp[i]) == NULL) continue; @@ -911,16 +999,19 @@ timer_lwpbind() void timer_exit(void) { - timer_t i; + uint_t i; proc_t *p = curproc; ASSERT(p->p_itimer != NULL); + ASSERT(p->p_itimer_sz != 0); - for (i = 0; i < timer_max; i++) - (void) timer_delete(i); + for (i = 0; i < p->p_itimer_sz; i++) { + (void) timer_delete((timer_t)i); + } - kmem_free(p->p_itimer, timer_max * sizeof (itimer_t *)); + kmem_free(p->p_itimer, p->p_itimer_sz * sizeof (itimer_t *)); p->p_itimer = NULL; + p->p_itimer_sz = 0; } /* |