Bug 1400563 - timer deadlock fix r=drno,dminor

timer deadlock fix

Differential Revision: https://phabricator.services.mozilla.com/D28087

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Nico Grunbaum 2019-04-23 05:32:57 +00:00
Родитель e14e511763
Коммит 8a57537454
8 изменённых файлов: 156 добавлений и 8 удалений

Просмотреть файл

@ -76,8 +76,27 @@ int sctp_get_tick_count(void) {
* SCTP_TIMERQ_LOCK protects:
* - SCTP_BASE_INFO(callqueue)
* - sctp_os_timer_next: next timer to check
* - sctp_os_timer_current: current callout callback in progress
* - sctp_os_timer_current_tid: current callout thread id in progress
* - sctp_os_timer_waiting: some thread is waiting for callout to complete
* - sctp_os_timer_wait_ctr: incremented every time a thread wants to wait
* for a callout to complete.
*/
static sctp_os_timer_t *sctp_os_timer_next = NULL;
static sctp_os_timer_t *sctp_os_timer_current = NULL;
static int sctp_os_timer_waiting = 0;
static int sctp_os_timer_wait_ctr = 0;
static userland_thread_id_t sctp_os_timer_current_tid;
/*
* SCTP_TIMERWAIT_LOCK (sctp_os_timerwait_mtx) protects:
* - sctp_os_timer_wait_cond: waiting for callout to complete
* - sctp_os_timer_done_ctr: value of "wait_ctr" after triggering "waiting"
*/
userland_mutex_t sctp_os_timerwait_mtx;
static userland_cond_t sctp_os_timer_wait_cond;
static int sctp_os_timer_done_ctr = 0;
void
sctp_os_timer_init(sctp_os_timer_t *c)
@ -95,6 +114,21 @@ sctp_os_timer_start(sctp_os_timer_t *c, int to_ticks, void (*ftn) (void *),
SCTP_TIMERQ_LOCK();
/* check to see if we're rescheduling a timer */
if (c == sctp_os_timer_current) {
/*
* We're being asked to reschedule a callout which is
* currently in progress.
*/
if (sctp_os_timer_waiting) {
/*
* This callout is already being stopped.
* callout. Don't reschedule.
*/
SCTP_TIMERQ_UNLOCK();
return;
}
}
if (c->c_flags & SCTP_CALLOUT_PENDING) {
if (c == sctp_os_timer_next) {
sctp_os_timer_next = TAILQ_NEXT(c, tqe);
@ -126,13 +160,51 @@ sctp_os_timer_start(sctp_os_timer_t *c, int to_ticks, void (*ftn) (void *),
int
sctp_os_timer_stop(sctp_os_timer_t *c)
{
int wakeup_cookie;
SCTP_TIMERQ_LOCK();
/*
* Don't attempt to delete a callout that's not on the queue.
*/
if (!(c->c_flags & SCTP_CALLOUT_PENDING)) {
c->c_flags &= ~SCTP_CALLOUT_ACTIVE;
SCTP_TIMERQ_UNLOCK();
if (sctp_os_timer_current != c) {
SCTP_TIMERQ_UNLOCK();
return (0);
} else {
/*
* Deleting the callout from the currently running
* callout from the same thread, so just return
*/
userland_thread_id_t tid;
sctp_userspace_thread_id(&tid);
if (sctp_userspace_thread_equal(tid,
sctp_os_timer_current_tid)) {
SCTP_TIMERQ_UNLOCK();
return (0);
}
/* need to wait until the callout is finished */
sctp_os_timer_waiting = 1;
wakeup_cookie = ++sctp_os_timer_wait_ctr;
SCTP_TIMERQ_UNLOCK();
SCTP_TIMERWAIT_LOCK();
/*
* wait only if sctp_handle_tick didn't do a wakeup
* in between the lock dance
*/
if (wakeup_cookie - sctp_os_timer_done_ctr > 0) {
#if defined (__Userspace_os_Windows)
SleepConditionVariableCS(&sctp_os_timer_wait_cond,
&sctp_os_timerwait_mtx,
INFINITE);
#else
pthread_cond_wait(&sctp_os_timer_wait_cond,
&sctp_os_timerwait_mtx);
#endif
}
SCTP_TIMERWAIT_UNLOCK();
}
return (0);
}
c->c_flags &= ~(SCTP_CALLOUT_ACTIVE | SCTP_CALLOUT_PENDING);
@ -150,6 +222,7 @@ sctp_handle_tick(int delta)
sctp_os_timer_t *c;
void (*c_func)(void *);
void *c_arg;
int wakeup_cookie;
SCTP_TIMERQ_LOCK();
/* update our tick count */
@ -162,9 +235,26 @@ sctp_handle_tick(int delta)
c_func = c->c_func;
c_arg = c->c_arg;
c->c_flags &= ~SCTP_CALLOUT_PENDING;
sctp_os_timer_current = c;
sctp_userspace_thread_id(&sctp_os_timer_current_tid);
SCTP_TIMERQ_UNLOCK();
c_func(c_arg);
SCTP_TIMERQ_LOCK();
sctp_os_timer_current = NULL;
if (sctp_os_timer_waiting) {
wakeup_cookie = sctp_os_timer_wait_ctr;
SCTP_TIMERQ_UNLOCK();
SCTP_TIMERWAIT_LOCK();
#if defined (__Userspace_os_Windows)
WakeAllConditionVariable(&sctp_os_timer_wait_cond);
#else
pthread_cond_broadcast(&sctp_os_timer_wait_cond);
#endif
sctp_os_timer_done_ctr = wakeup_cookie;
SCTP_TIMERWAIT_UNLOCK();
SCTP_TIMERQ_LOCK();
sctp_os_timer_waiting = 0;
}
c = sctp_os_timer_next;
} else {
c = TAILQ_NEXT(c, tqe);
@ -217,6 +307,14 @@ sctp_start_timer(void)
*/
int rc;
#if defined(__Userspace_os_Windows)
InitializeConditionVariable(&sctp_os_timer_wait_cond);
#else
rc = pthread_cond_init(&sctp_os_timer_wait_cond, NULL);
if (rc)
SCTP_PRINTF("ERROR; return code from pthread_cond_init is %d\n", rc);
#endif
rc = sctp_userspace_thread_create(&SCTP_BASE_VAR(timer_thread), user_sctp_timer_iterate);
if (rc) {
SCTP_PRINTF("ERROR; return code from sctp_thread_create() is %d\n", rc);

Просмотреть файл

@ -52,22 +52,35 @@ __FBSDID("$FreeBSD$");
#define SCTP_TICKS_PER_FASTTIMO 20 /* called about every 20ms */
extern userland_mutex_t sctp_os_timerwait_mtx;
#if defined(__Userspace__)
#if defined(__Userspace_os_Windows)
#define SCTP_TIMERQ_LOCK() EnterCriticalSection(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERQ_UNLOCK() LeaveCriticalSection(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERQ_LOCK_INIT() InitializeCriticalSection(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERQ_LOCK_DESTROY() DeleteCriticalSection(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERWAIT_LOCK() EnterCriticalSection(&sctp_os_timerwait_mtx)
#define SCTP_TIMERWAIT_UNLOCK() LeaveCriticalSection(&sctp_os_timerwait_mtx)
#define SCTP_TIMERWAIT_LOCK_INIT() InitializeCriticalSection(&sctp_os_timerwait_mtx)
#define SCTP_TIMERWAIT_LOCK_DESTROY() DeleteCriticalSection(&sctp_os_timerwait_mtx)
#else
#ifdef INVARIANTS
#define SCTP_TIMERQ_LOCK() KASSERT(pthread_mutex_lock(&SCTP_BASE_VAR(timer_mtx)) == 0, ("%s: timer_mtx already locked", __func__))
#define SCTP_TIMERQ_UNLOCK() KASSERT(pthread_mutex_unlock(&SCTP_BASE_VAR(timer_mtx)) == 0, ("%s: timer_mtx not locked", __func__))
#define SCTP_TIMERWAIT_LOCK() KASSERT(pthread_mutex_lock(&sctp_os_timerwait_mtx) == 0, ("%s: sctp_os_timerwait_mtx already locked", __func__))
#define SCTP_TIMERWAIT_UNLOCK() KASSERT(pthread_mutex_unlock(&sctp_os_timerwait_mtx) == 0, ("%s: sctp_os_timerwait_mtx not locked", __func__))
#else
#define SCTP_TIMERQ_LOCK() (void)pthread_mutex_lock(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERQ_UNLOCK() (void)pthread_mutex_unlock(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERWAIT_LOCK() (void)pthread_mutex_lock(&sctp_os_timerwait_mtx)
#define SCTP_TIMERWAIT_UNLOCK() (void)pthread_mutex_unlock(&sctp_os_timerwait_mtx)
#endif
#define SCTP_TIMERQ_LOCK_INIT() (void)pthread_mutex_init(&SCTP_BASE_VAR(timer_mtx), &SCTP_BASE_VAR(mtx_attr))
#define SCTP_TIMERQ_LOCK_DESTROY() (void)pthread_mutex_destroy(&SCTP_BASE_VAR(timer_mtx))
#define SCTP_TIMERWAIT_LOCK_INIT() (void)pthread_mutex_init(&sctp_os_timerwait_mtx, &SCTP_BASE_VAR(mtx_attr))
#define SCTP_TIMERWAIT_LOCK_DESTROY() (void)pthread_mutex_destroy(&sctp_os_timerwait_mtx)
#endif
#endif

Просмотреть файл

@ -34,7 +34,7 @@
#ifdef __FreeBSD__
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/netinet/sctp_indata.c 325434 2017-11-05 11:59:33Z tuexen $");
__FBSDID("$FreeBSD: head/sys/netinet/sctp_indata.c 345494 2019-03-25 09:47:22Z tuexen $");
#endif
#include <netinet/sctp_os.h>

Просмотреть файл

@ -76,6 +76,7 @@ void WakeAllXPConditionVariable(userland_cond_t *);
typedef CONDITION_VARIABLE userland_cond_t;
#endif
typedef HANDLE userland_thread_t;
typedef DWORD userland_thread_id_t;
#define ADDRESS_FAMILY unsigned __int8
#define IPVERSION 4
#define MAXTTL 255
@ -282,6 +283,7 @@ typedef char* caddr_t;
typedef pthread_mutex_t userland_mutex_t;
typedef pthread_cond_t userland_cond_t;
typedef pthread_t userland_thread_t;
typedef pthread_t userland_thread_id_t;
#endif
#if defined(__Userspace_os_Windows) || defined(__Userspace_os_NaCl)
@ -1039,6 +1041,9 @@ sctp_userspace_thread_create(userland_thread_t *thread, start_routine_t start_ro
void
sctp_userspace_set_threadname(const char *name);
int sctp_userspace_thread_id(userland_thread_id_t *thread);
int sctp_userspace_thread_equal(userland_thread_id_t t1, userland_thread_id_t t2);
/*
* SCTP protocol specific mbuf flags.
*/

Просмотреть файл

@ -34,7 +34,7 @@
#ifdef __FreeBSD__
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/netinet/sctp_pcb.c 334532 2018-06-02 16:28:10Z tuexen $");
__FBSDID("$FreeBSD: head/sys/netinet/sctp_pcb.c 345504 2019-03-25 15:23:20Z tuexen $");
#endif
#include <netinet/sctp_os.h>
@ -5760,6 +5760,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
* in case.
*/
/* anything on the wheel needs to be removed */
SCTP_TCB_SEND_LOCK(stcb);
for (i = 0; i < asoc->streamoutcnt; i++) {
struct sctp_stream_out *outs;
@ -5768,7 +5769,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
TAILQ_FOREACH_SAFE(sp, &outs->outqueue, next, nsp) {
atomic_subtract_int(&asoc->stream_queue_cnt, 1);
TAILQ_REMOVE(&outs->outqueue, sp, next);
stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, outs, sp, 0);
stcb->asoc.ss_functions.sctp_ss_remove_from_stream(stcb, asoc, outs, sp, 1);
sctp_free_spbufspace(stcb, asoc, sp);
if (sp->data) {
if (so) {
@ -5790,6 +5791,7 @@ sctp_free_assoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb, int from_inpcbfre
sctp_free_a_strmoq(stcb, sp, SCTP_SO_LOCKED);
}
}
SCTP_TCB_SEND_UNLOCK(stcb);
/*sa_ignore FREED_MEMORY*/
TAILQ_FOREACH_SAFE(strrst, &asoc->resetHead, next_resp, nstrrst) {
TAILQ_REMOVE(&asoc->resetHead, strrst, next_resp);
@ -6848,6 +6850,7 @@ sctp_pcb_init()
#if defined(_SCTP_NEEDS_CALLOUT_) || defined(_USER_SCTP_NEEDS_CALLOUT_)
/* allocate the lock for the callout/timer queue */
SCTP_TIMERQ_LOCK_INIT();
SCTP_TIMERWAIT_LOCK_INIT();
TAILQ_INIT(&SCTP_BASE_INFO(callqueue));
#endif
#if defined(__Userspace__)
@ -7042,6 +7045,7 @@ retry:
/* free the locks and mutexes */
#if defined(__APPLE__)
SCTP_TIMERQ_LOCK_DESTROY();
SCTP_TIMERWAIT_LOCK_DESTROY();
#endif
#ifdef SCTP_PACKET_LOGGING
SCTP_IP_PKTLOG_DESTROY();
@ -7068,6 +7072,7 @@ retry:
#endif
#if defined(__Userspace__)
SCTP_TIMERQ_LOCK_DESTROY();
SCTP_TIMERWAIT_LOCK_DESTROY();
SCTP_ZONE_DESTROY(zone_mbuf);
SCTP_ZONE_DESTROY(zone_clust);
SCTP_ZONE_DESTROY(zone_ext_refcnt);

Просмотреть файл

@ -30,7 +30,7 @@
#ifdef __FreeBSD__
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/netinet/sctp_ss_functions.c 326180 2017-11-24 19:38:59Z tuexen $");
__FBSDID("$FreeBSD: head/sys/netinet/sctp_ss_functions.c 345505 2019-03-25 16:40:54Z tuexen $");
#endif
#include <netinet/sctp_pcb.h>
@ -820,6 +820,8 @@ sctp_ss_fcfs_init_stream(struct sctp_tcb *stcb, struct sctp_stream_out *strq, st
stcb->asoc.ss_data.last_out_stream = strq;
}
}
strq->ss_params.fb.next_spoke.tqe_next = NULL;
strq->ss_params.fb.next_spoke.tqe_prev = NULL;
return;
}

Просмотреть файл

@ -61,12 +61,39 @@ sctp_userspace_thread_create(userland_thread_t *thread, start_routine_t start_ro
return GetLastError();
return 0;
}
int
sctp_userspace_thread_id(userland_thread_id_t *thread)
{
*thread = GetCurrentThreadId();
return 0;
}
int
sctp_userspace_thread_equal(userland_thread_id_t t1, userland_thread_id_t t2)
{
return (t1 == t2);
}
#else
int
sctp_userspace_thread_create(userland_thread_t *thread, start_routine_t start_routine)
{
return pthread_create(thread, NULL, start_routine, NULL);
}
int
sctp_userspace_thread_id(userland_thread_id_t *thread)
{
*thread = pthread_self();
return 0;
}
int
sctp_userspace_thread_equal(userland_thread_id_t t1, userland_thread_id_t t2)
{
return pthread_equal(t1, t2);
}
#endif
void

Просмотреть файл

@ -34,7 +34,7 @@
#ifdef __FreeBSD__
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: head/sys/netinet/sctp_usrreq.c 325370 2017-11-03 20:46:12Z tuexen $");
__FBSDID("$FreeBSD: head/sys/netinet/sctp_usrreq.c 345525 2019-03-26 08:27:00Z tuexen $");
#endif
#include <netinet/sctp_os.h>
@ -2081,8 +2081,6 @@ sctp_do_connect_x(struct socket *so, struct sctp_inpcb *inp, void *optval,
sctp_connectx_helper_add(stcb, sa, (totaddr-1), &error);
/* Fill in the return id */
if (error) {
(void)sctp_free_assoc(inp, stcb, SCTP_PCBFREE_FORCE,
SCTP_FROM_SCTP_USRREQ + SCTP_LOC_7);
goto out_now;
}
a_id = (sctp_assoc_t *)optval;