diff options
-rw-r--r-- | sys/kern/kern_lock.c | 28 | ||||
-rw-r--r-- | sys/kern/kern_sx.c | 55 | ||||
-rw-r--r-- | sys/kern/subr_turnstile.c | 67 | ||||
-rw-r--r-- | sys/sys/lockmgr.h | 3 | ||||
-rw-r--r-- | sys/sys/sx.h | 3 |
5 files changed, 155 insertions, 1 deletions
diff --git a/sys/kern/kern_lock.c b/sys/kern/kern_lock.c index e9cff2e..c0b929a 100644 --- a/sys/kern/kern_lock.c +++ b/sys/kern/kern_lock.c @@ -589,6 +589,34 @@ lockmgr_printinfo(lkp) } #ifdef DDB +/* + * Check to see if a thread that is blocked on a sleep queue is actually + * blocked on a 'struct lock'. If so, output some details and return true. + * If the lock has an exclusive owner, return that in *ownerp. + */ +int +lockmgr_chain(struct thread *td, struct thread **ownerp) +{ + struct lock *lkp; + + lkp = td->td_wchan; + + /* Simple test to see if wchan points to a lockmgr lock. */ + if (lkp->lk_wmesg != td->td_wmesg) + return (0); + + /* Ok, we think we have a lockmgr lock, so output some details. */ + db_printf("blocked on lk \"%s\" ", lkp->lk_wmesg); + if (lkp->lk_sharecount) { + db_printf("SHARED (count %d)\n", lkp->lk_sharecount); + *ownerp = NULL; + } else { + db_printf("EXCL (count %d)\n", lkp->lk_exclusivecount); + *ownerp = lkp->lk_lockholder; + } + return (1); +} + DB_SHOW_COMMAND(lockmgr, db_show_lockmgr) { struct thread *td; diff --git a/sys/kern/kern_sx.c b/sys/kern/kern_sx.c index d215807..fdd8bc9 100644 --- a/sys/kern/kern_sx.c +++ b/sys/kern/kern_sx.c @@ -48,9 +48,9 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/sx.h> +#ifdef DDB #include <ddb/ddb.h> -#ifdef DDB static void db_show_sx(struct lock_object *lock); #endif @@ -395,4 +395,57 @@ db_show_sx(struct lock_object *lock) db_printf(" waiters: %d shared, %d exclusive\n", sx->sx_shrd_wcnt, sx->sx_excl_wcnt); } + +/* + * Check to see if a thread that is blocked on a sleep queue is actually + * blocked on an sx lock. If so, output some details and return true. + * If the lock has an exclusive owner, return that in *ownerp. + */ +int +sx_chain(struct thread *td, struct thread **ownerp) +{ + struct sx *sx; + struct cv *cv; + + /* + * First, see if it looks like td is blocked on a condition + * variable. + */ + cv = td->td_wchan; + if (cv->cv_description != td->td_wmesg) + return (0); + + /* + * Ok, see if it looks like td is blocked on the exclusive + * condition variable. + */ + sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_excl_cv)); + if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx && + sx->sx_excl_wcnt > 0) + goto ok; + + /* + * Second, see if it looks like td is blocked on the shared + * condition variable. + */ + sx = (struct sx *)((char *)cv - offsetof(struct sx, sx_shrd_cv)); + if (LOCK_CLASS(&sx->sx_object) == &lock_class_sx && + sx->sx_shrd_wcnt > 0) + goto ok; + + /* Doesn't seem to be an sx lock. */ + return (0); + +ok: + /* We think we have an sx lock, so output some details. */ + db_printf("blocked on sx \"%s\" ", td->td_wmesg); + if (sx->sx_cnt >= 0) { + db_printf("SLOCK (count %d)\n", sx->sx_cnt); + *ownerp = NULL; + } else { + db_printf("XLOCK\n"); + *ownerp = sx->sx_xholder; + } + return (1); +} #endif diff --git a/sys/kern/subr_turnstile.c b/sys/kern/subr_turnstile.c index 0866999..ec659ae 100644 --- a/sys/kern/subr_turnstile.c +++ b/sys/kern/subr_turnstile.c @@ -78,6 +78,8 @@ __FBSDID("$FreeBSD$"); #ifdef DDB #include <sys/kdb.h> #include <ddb/ddb.h> +#include <sys/lockmgr.h> +#include <sys/sx.h> #endif /* @@ -1120,6 +1122,71 @@ DB_SHOW_COMMAND(allchains, db_show_allchains) } } +/* + * Show all the threads a particular thread is waiting on based on + * sleepable locks. + */ +static void +print_sleepchain(struct thread *td, const char *prefix) +{ + struct thread *owner; + + /* + * Follow the chain. We keep walking as long as the thread is + * blocked on a sleep lock that has an owner. + */ + while (!db_pager_quit) { + db_printf("%sthread %d (pid %d, %s) ", prefix, td->td_tid, + td->td_proc->p_pid, td->td_name[0] != '\0' ? td->td_name : + td->td_proc->p_comm); + switch (td->td_state) { + case TDS_INACTIVE: + db_printf("is inactive\n"); + return; + case TDS_CAN_RUN: + db_printf("can run\n"); + return; + case TDS_RUNQ: + db_printf("is on a run queue\n"); + return; + case TDS_RUNNING: + db_printf("running on CPU %d\n", td->td_oncpu); + return; + case TDS_INHIBITED: + if (TD_ON_SLEEPQ(td)) { + if (lockmgr_chain(td, &owner) || + sx_chain(td, &owner)) { + if (owner == NULL) + return; + td = owner; + break; + } + db_printf("sleeping on %p \"%s\"\n", + td->td_wchan, td->td_wmesg); + return; + } + db_printf("inhibited\n"); + return; + default: + db_printf("??? (%#x)\n", td->td_state); + return; + } + } +} + +DB_SHOW_COMMAND(sleepchain, db_show_sleepchain) +{ + struct thread *td; + + /* Figure out which thread to start with. */ + if (have_addr) + td = db_lookup_thread(addr, TRUE); + else + td = kdb_thread; + + print_sleepchain(td, ""); +} + static void print_waiters(struct turnstile *ts, int indent); static void diff --git a/sys/sys/lockmgr.h b/sys/sys/lockmgr.h index 57d2f36..dd0eed9 100644 --- a/sys/sys/lockmgr.h +++ b/sys/sys/lockmgr.h @@ -203,5 +203,8 @@ void transferlockers(struct lock *, struct lock *); void lockmgr_printinfo(struct lock *); int lockstatus(struct lock *, struct thread *); int lockcount(struct lock *); +#ifdef DDB +int lockmgr_chain(struct thread *td, struct thread **ownerp); +#endif #endif /* !_SYS_LOCKMGR_H_ */ diff --git a/sys/sys/sx.h b/sys/sys/sx.h index 4ee045a..0abd94d 100644 --- a/sys/sys/sx.h +++ b/sys/sys/sx.h @@ -60,6 +60,9 @@ void _sx_downgrade(struct sx *sx, const char *file, int line); #ifdef INVARIANT_SUPPORT void _sx_assert(struct sx *sx, int what, const char *file, int line); #endif +#ifdef DDB +int sx_chain(struct thread *td, struct thread **ownerp); +#endif struct sx_args { struct sx *sa_sx; |