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"

Reply via email to