Author: mjg Date: Wed Nov 11 08:50:04 2020 New Revision: 367584 URL: https://svnweb.freebsd.org/changeset/base/367584
Log: thread: rework tidhash vs proc lock interaction Apart from minor clean up this gets rid of proc unlock/lock cycle on thread exit to work around LOR against tidhash lock. Modified: head/sys/kern/init_main.c head/sys/kern/kern_kthread.c head/sys/kern/kern_thr.c head/sys/kern/kern_thread.c head/sys/sys/proc.h Modified: head/sys/kern/init_main.c ============================================================================== --- head/sys/kern/init_main.c Wed Nov 11 08:48:43 2020 (r367583) +++ head/sys/kern/init_main.c Wed Nov 11 08:50:04 2020 (r367584) @@ -497,7 +497,7 @@ proc0_init(void *dummy __unused) STAILQ_INIT(&p->p_ktr); p->p_nice = NZERO; td->td_tid = THREAD0_TID; - LIST_INSERT_HEAD(TIDHASH(td->td_tid), td, td_hash); + tidhash_add(td); td->td_state = TDS_RUNNING; td->td_pri_class = PRI_TIMESHARE; td->td_user_pri = PUSER; Modified: head/sys/kern/kern_kthread.c ============================================================================== --- head/sys/kern/kern_kthread.c Wed Nov 11 08:48:43 2020 (r367583) +++ head/sys/kern/kern_kthread.c Wed Nov 11 08:50:04 2020 (r367584) @@ -350,15 +350,12 @@ kthread_exit(void) * The last exiting thread in a kernel process must tear down * the whole process. */ - rw_wlock(&tidhash_lock); PROC_LOCK(p); if (p->p_numthreads == 1) { PROC_UNLOCK(p); - rw_wunlock(&tidhash_lock); kproc_exit(0); } - LIST_REMOVE(td, td_hash); - rw_wunlock(&tidhash_lock); + tidhash_remove(td); umtx_thread_exit(td); tdsigcleanup(td); PROC_SLOCK(p); Modified: head/sys/kern/kern_thr.c ============================================================================== --- head/sys/kern/kern_thr.c Wed Nov 11 08:48:43 2020 (r367583) +++ head/sys/kern/kern_thr.c Wed Nov 11 08:50:04 2020 (r367584) @@ -353,14 +353,13 @@ kern_thr_exit(struct thread *td) return (0); } - p->p_pendingexits++; td->td_dbgflags |= TDB_EXIT; - if (p->p_ptevents & PTRACE_LWP) + if (p->p_ptevents & PTRACE_LWP) { + p->p_pendingexits++; ptracestop(td, SIGTRAP, NULL); - PROC_UNLOCK(p); + p->p_pendingexits--; + } tidhash_remove(td); - PROC_LOCK(p); - p->p_pendingexits--; /* * The check above should prevent all other threads from this Modified: head/sys/kern/kern_thread.c ============================================================================== --- head/sys/kern/kern_thread.c Wed Nov 11 08:48:43 2020 (r367583) +++ head/sys/kern/kern_thread.c Wed Nov 11 08:50:04 2020 (r367584) @@ -147,9 +147,10 @@ SYSCTL_INT(_kern, OID_AUTO, maxthread, CTLFLAG_RDTUN, static int nthreads; -struct tidhashhead *tidhashtbl; -u_long tidhash; -struct rwlock tidhash_lock; +static LIST_HEAD(tidhashhead, thread) *tidhashtbl; +static u_long tidhash; +static struct rwlock tidhash_lock; +#define TIDHASH(tid) (&tidhashtbl[(tid) & tidhash]) EVENTHANDLER_LIST_DEFINE(thread_ctor); EVENTHANDLER_LIST_DEFINE(thread_dtor); @@ -1329,13 +1330,65 @@ thread_single_end(struct proc *p, int mode) kick_proc0(); } -/* Locate a thread by number; return with proc lock held. */ +/* + * Locate a thread by number and return with proc lock held. + * + * thread exit establishes proc -> tidhash lock ordering, but lookup + * takes tidhash first and needs to return locked proc. + * + * The problem is worked around by relying on type-safety of both + * structures and doing the work in 2 steps: + * - tidhash-locked lookup which saves both thread and proc pointers + * - proc-locked verification that the found thread still matches + */ +static bool +tdfind_hash(lwpid_t tid, pid_t pid, struct proc **pp, struct thread **tdp) +{ +#define RUN_THRESH 16 + struct proc *p; + struct thread *td; + int run; + bool locked; + + run = 0; + rw_rlock(&tidhash_lock); + locked = true; + LIST_FOREACH(td, TIDHASH(tid), td_hash) { + if (td->td_tid != tid) { + run++; + continue; + } + p = td->td_proc; + if (pid != -1 && p->p_pid != pid) { + td = NULL; + break; + } + if (run > RUN_THRESH) { + if (rw_try_upgrade(&tidhash_lock)) { + LIST_REMOVE(td, td_hash); + LIST_INSERT_HEAD(TIDHASH(td->td_tid), + td, td_hash); + rw_wunlock(&tidhash_lock); + locked = false; + break; + } + } + break; + } + if (locked) + rw_runlock(&tidhash_lock); + if (td == NULL) + return (false); + *pp = p; + *tdp = td; + return (true); +} + struct thread * tdfind(lwpid_t tid, pid_t pid) { -#define RUN_THRESH 16 + struct proc *p; struct thread *td; - int run = 0; td = curthread; if (td->td_tid == tid) { @@ -1345,34 +1398,24 @@ tdfind(lwpid_t tid, pid_t pid) return (td); } - rw_rlock(&tidhash_lock); - LIST_FOREACH(td, TIDHASH(tid), td_hash) { - if (td->td_tid == tid) { - if (pid != -1 && td->td_proc->p_pid != pid) { - td = NULL; - break; - } - PROC_LOCK(td->td_proc); - if (td->td_proc->p_state == PRS_NEW) { - PROC_UNLOCK(td->td_proc); - td = NULL; - break; - } - if (run > RUN_THRESH) { - if (rw_try_upgrade(&tidhash_lock)) { - LIST_REMOVE(td, td_hash); - LIST_INSERT_HEAD(TIDHASH(td->td_tid), - td, td_hash); - rw_wunlock(&tidhash_lock); - return (td); - } - } - break; + for (;;) { + if (!tdfind_hash(tid, pid, &p, &td)) + return (NULL); + PROC_LOCK(p); + if (td->td_tid != tid) { + PROC_UNLOCK(p); + continue; } - run++; + if (td->td_proc != p) { + PROC_UNLOCK(p); + continue; + } + if (p->p_state == PRS_NEW) { + PROC_UNLOCK(p); + return (NULL); + } + return (td); } - rw_runlock(&tidhash_lock); - return (td); } void Modified: head/sys/sys/proc.h ============================================================================== --- head/sys/sys/proc.h Wed Nov 11 08:48:43 2020 (r367583) +++ head/sys/sys/proc.h Wed Nov 11 08:50:04 2020 (r367584) @@ -968,10 +968,6 @@ extern LIST_HEAD(pidhashhead, proc) *pidhashtbl; extern struct sx *pidhashtbl_lock; extern u_long pidhash; extern u_long pidhashlock; -#define TIDHASH(tid) (&tidhashtbl[(tid) & tidhash]) -extern LIST_HEAD(tidhashhead, thread) *tidhashtbl; -extern u_long tidhash; -extern struct rwlock tidhash_lock; #define PGRPHASH(pgid) (&pgrphashtbl[(pgid) & pgrphash]) extern LIST_HEAD(pgrphashhead, pgrp) *pgrphashtbl; _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"