Date: Thu, 5 Jan 2006 11:22:16 -0800 (PST) From: Don Lewis <truckman@FreeBSD.org> To: stable@FreeBSD.org Subject: WITNESS speedup patch for RELENG_5 Message-ID: <200601051922.k05JMGMc015613@gw.catspoiler.org>
next in thread | raw e-mail | index | archive | help
If you are running RELENG_5 and using WITNESS, you might want to try the patch below. It speeds up WITNESS rather dramatically. This patch was committed to HEAD in late August (subr_witness.c 1.198) and early September (subr_witness.c 1.200). It was MFC'ed to RELENG_6 in the last few days. I'd like to MFC it to RELENG_5, but I think it should get a bit more exposure before I do. Index: sys/kern/subr_witness.c =================================================================== RCS file: /home/ncvs/src/sys/kern/subr_witness.c,v retrieving revision 1.178.2.8 diff -u -r1.178.2.8 subr_witness.c --- sys/kern/subr_witness.c 4 May 2005 19:26:30 -0000 1.178.2.8 +++ sys/kern/subr_witness.c 12 Sep 2005 04:52:53 -0000 @@ -165,16 +165,9 @@ static int isitmychild(struct witness *parent, struct witness *child); static int isitmydescendant(struct witness *parent, struct witness *child); static int itismychild(struct witness *parent, struct witness *child); -static int rebalancetree(struct witness_list *list); static void removechild(struct witness *parent, struct witness *child); -static int reparentchildren(struct witness *newparent, - struct witness *oldparent); static int sysctl_debug_witness_watch(SYSCTL_HANDLER_ARGS); -static void witness_displaydescendants(void(*)(const char *fmt, ...), - struct witness *, int indent); static const char *fixup_filename(const char *file); -static void witness_leveldescendents(struct witness *parent, int level); -static void witness_levelall(void); static struct witness *witness_get(void); static void witness_free(struct witness *m); static struct witness_child_list_entry *witness_child_get(void); @@ -185,20 +178,21 @@ struct lock_object *lock); static void witness_list_lock(struct lock_instance *instance); #ifdef DDB -static void witness_list(struct thread *td); +static void witness_leveldescendents(struct witness *parent, int level); +static void witness_levelall(void); +static void witness_displaydescendants(void(*)(const char *fmt, ...), + struct witness *, int indent); static void witness_display_list(void(*prnt)(const char *fmt, ...), struct witness_list *list); static void witness_display(void(*)(const char *fmt, ...)); +static void witness_list(struct thread *td); #endif SYSCTL_NODE(_debug, OID_AUTO, witness, CTLFLAG_RW, 0, "Witness Locking"); /* - * If set to 0, witness is disabled. If set to 1, witness performs full lock - * order checking for all locks. If set to 2 or higher, then witness skips - * the full lock order check if the lock being acquired is at a higher level - * (i.e. farther down in the tree) than the current lock. This last mode is - * somewhat experimental and not considered fully safe. At runtime, this + * If set to 0, witness is disabled. If set to a non-zero value, witness + * performs full lock order checking for all locks. At runtime, this * value may be set to 0 to turn off witness. witness is not allowed be * turned on once it is turned off, however. */ @@ -250,6 +244,16 @@ static struct witness_child_list_entry *w_child_free = NULL; static struct lock_list_entry *w_lock_list_free = NULL; +static int w_free_cnt, w_spin_cnt, w_sleep_cnt, w_child_free_cnt, w_child_cnt; +SYSCTL_INT(_debug_witness, OID_AUTO, free_cnt, CTLFLAG_RD, &w_free_cnt, 0, ""); +SYSCTL_INT(_debug_witness, OID_AUTO, spin_cnt, CTLFLAG_RD, &w_spin_cnt, 0, ""); +SYSCTL_INT(_debug_witness, OID_AUTO, sleep_cnt, CTLFLAG_RD, &w_sleep_cnt, 0, + ""); +SYSCTL_INT(_debug_witness, OID_AUTO, child_free_cnt, CTLFLAG_RD, + &w_child_free_cnt, 0, ""); +SYSCTL_INT(_debug_witness, OID_AUTO, child_cnt, CTLFLAG_RD, &w_child_cnt, 0, + ""); + static struct witness w_data[WITNESS_COUNT]; static struct witness_child_list_entry w_childdata[WITNESS_CHILDCOUNT]; static struct lock_list_entry w_locklistdata[LOCK_CHILDCOUNT]; @@ -575,6 +579,87 @@ #ifdef DDB static void +witness_levelall (void) +{ + struct witness_list *list; + struct witness *w, *w1; + + /* + * First clear all levels. + */ + STAILQ_FOREACH(w, &w_all, w_list) { + w->w_level = 0; + } + + /* + * Look for locks with no parent and level all their descendants. + */ + STAILQ_FOREACH(w, &w_all, w_list) { + /* + * This is just an optimization, technically we could get + * away just walking the all list each time. + */ + if (w->w_class->lc_flags & LC_SLEEPLOCK) + list = &w_sleep; + else + list = &w_spin; + STAILQ_FOREACH(w1, list, w_typelist) { + if (isitmychild(w1, w)) + goto skip; + } + witness_leveldescendents(w, 0); + skip: + ; /* silence GCC 3.x */ + } +} + +static void +witness_leveldescendents(struct witness *parent, int level) +{ + struct witness_child_list_entry *wcl; + int i; + + if (parent->w_level < level) + parent->w_level = level; + level++; + for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) + for (i = 0; i < wcl->wcl_count; i++) + witness_leveldescendents(wcl->wcl_children[i], level); +} + +static void +witness_displaydescendants(void(*prnt)(const char *fmt, ...), + struct witness *parent, int indent) +{ + struct witness_child_list_entry *wcl; + int i, level; + + level = parent->w_level; + prnt("%-2d", level); + for (i = 0; i < indent; i++) + prnt(" "); + if (parent->w_refcount > 0) + prnt("%s", parent->w_name); + else + prnt("(dead)"); + if (parent->w_displayed) { + prnt(" -- (already displayed)\n"); + return; + } + parent->w_displayed = 1; + if (parent->w_refcount > 0) { + if (parent->w_file != NULL) + prnt(" -- last acquired @ %s:%d", parent->w_file, + parent->w_line); + } + prnt("\n"); + for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) + for (i = 0; i < wcl->wcl_count; i++) + witness_displaydescendants(prnt, + wcl->wcl_children[i], indent + 1); +} + +static void witness_display_list(void(*prnt)(const char *fmt, ...), struct witness_list *list) { @@ -795,18 +880,11 @@ MPASS(!mtx_owned(&w_mtx)); mtx_lock_spin(&w_mtx); /* - * If we have a known higher number just say ok - */ - if (witness_watch > 1 && w->w_level > w1->w_level) { - mtx_unlock_spin(&w_mtx); - return; - } - /* * If we know that the the lock we are acquiring comes after * the lock we most recently acquired in the lock order tree, * then there is no need for any further checks. */ - if (isitmydescendant(w1, w)) { + if (isitmychild(w1, w)) { mtx_unlock_spin(&w_mtx); return; } @@ -1287,11 +1365,13 @@ w->w_class = lock_class; w->w_refcount = 1; STAILQ_INSERT_HEAD(&w_all, w, w_list); - if (lock_class->lc_flags & LC_SPINLOCK) + if (lock_class->lc_flags & LC_SPINLOCK) { STAILQ_INSERT_HEAD(&w_spin, w, w_typelist); - else if (lock_class->lc_flags & LC_SLEEPLOCK) + w_spin_cnt++; + } else if (lock_class->lc_flags & LC_SLEEPLOCK) { STAILQ_INSERT_HEAD(&w_sleep, w, w_typelist); - else { + w_sleep_cnt++; + } else { mtx_unlock_spin(&w_mtx); panic("lock class %s is not sleep or spin", lock_class->lc_name); @@ -1309,10 +1389,13 @@ struct witness *parent; MPASS(w->w_refcount == 0); - if (w->w_class->lc_flags & LC_SLEEPLOCK) + if (w->w_class->lc_flags & LC_SLEEPLOCK) { list = &w_sleep; - else + w_sleep_cnt--; + } else { list = &w_spin; + w_spin_cnt--; + } /* * First, we run through the entire tree looking for any * witnesses that the outgoing witness is a child of. For @@ -1323,8 +1406,6 @@ if (!isitmychild(parent, w)) continue; removechild(parent, w); - if (!reparentchildren(parent, w)) - return (0); } /* @@ -1333,6 +1414,7 @@ */ for (wcl = w->w_children; wcl != NULL; wcl = nwcl) { nwcl = wcl->wcl_next; + w_child_cnt--; witness_child_free(wcl); } @@ -1343,34 +1425,6 @@ STAILQ_REMOVE(&w_all, w, witness, w_list); witness_free(w); - /* Finally, fixup the tree. */ - return (rebalancetree(list)); -} - -/* - * Prune an entire lock order tree. We look for cases where a lock - * is now both a descendant and a direct child of a given lock. In - * that case, we want to remove the direct child link from the tree. - * - * Returns false if insertchild() fails. - */ -static int -rebalancetree(struct witness_list *list) -{ - struct witness *child, *parent; - - STAILQ_FOREACH(child, list, w_typelist) { - STAILQ_FOREACH(parent, list, w_typelist) { - if (!isitmychild(parent, child)) - continue; - removechild(parent, child); - if (isitmydescendant(parent, child)) - continue; - if (!insertchild(parent, child)) - return (0); - } - } - witness_levelall(); return (1); } @@ -1395,31 +1449,13 @@ *wcl = witness_child_get(); if (*wcl == NULL) return (0); + w_child_cnt++; } (*wcl)->wcl_children[(*wcl)->wcl_count++] = child; return (1); } -/* - * Make all the direct descendants of oldparent be direct descendants - * of newparent. - */ -static int -reparentchildren(struct witness *newparent, struct witness *oldparent) -{ - struct witness_child_list_entry *wcl; - int i; - - /* Avoid making a witness a child of itself. */ - MPASS(!isitmychild(oldparent, newparent)); - - for (wcl = oldparent->w_children; wcl != NULL; wcl = wcl->wcl_next) - for (i = 0; i < wcl->wcl_count; i++) - if (!insertchild(newparent, wcl->wcl_children[i])) - return (0); - return (1); -} static int itismychild(struct witness *parent, struct witness *child) @@ -1441,7 +1477,7 @@ list = &w_sleep; else list = &w_spin; - return (rebalancetree(list)); + return (1); } static void @@ -1465,6 +1501,7 @@ return; wcl1 = *wcl; *wcl = wcl1->wcl_next; + w_child_cnt--; witness_child_free(wcl1); } @@ -1503,87 +1540,6 @@ return (0); } -static void -witness_levelall (void) -{ - struct witness_list *list; - struct witness *w, *w1; - - /* - * First clear all levels. - */ - STAILQ_FOREACH(w, &w_all, w_list) { - w->w_level = 0; - } - - /* - * Look for locks with no parent and level all their descendants. - */ - STAILQ_FOREACH(w, &w_all, w_list) { - /* - * This is just an optimization, technically we could get - * away just walking the all list each time. - */ - if (w->w_class->lc_flags & LC_SLEEPLOCK) - list = &w_sleep; - else - list = &w_spin; - STAILQ_FOREACH(w1, list, w_typelist) { - if (isitmychild(w1, w)) - goto skip; - } - witness_leveldescendents(w, 0); - skip: - ; /* silence GCC 3.x */ - } -} - -static void -witness_leveldescendents(struct witness *parent, int level) -{ - struct witness_child_list_entry *wcl; - int i; - - if (parent->w_level < level) - parent->w_level = level; - level++; - for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) - for (i = 0; i < wcl->wcl_count; i++) - witness_leveldescendents(wcl->wcl_children[i], level); -} - -static void -witness_displaydescendants(void(*prnt)(const char *fmt, ...), - struct witness *parent, int indent) -{ - struct witness_child_list_entry *wcl; - int i, level; - - level = parent->w_level; - prnt("%-2d", level); - for (i = 0; i < indent; i++) - prnt(" "); - if (parent->w_refcount > 0) - prnt("%s", parent->w_name); - else - prnt("(dead)"); - if (parent->w_displayed) { - prnt(" -- (already displayed)\n"); - return; - } - parent->w_displayed = 1; - if (parent->w_refcount > 0) { - if (parent->w_file != NULL) - prnt(" -- last acquired @ %s:%d", parent->w_file, - parent->w_line); - } - prnt("\n"); - for (wcl = parent->w_children; wcl != NULL; wcl = wcl->wcl_next) - for (i = 0; i < wcl->wcl_count; i++) - witness_displaydescendants(prnt, - wcl->wcl_children[i], indent + 1); -} - #ifdef BLESSING static int blessed(struct witness *w1, struct witness *w2) @@ -1623,6 +1579,7 @@ } w = STAILQ_FIRST(&w_free); STAILQ_REMOVE_HEAD(&w_free, w_list); + w_free_cnt--; bzero(w, sizeof(*w)); return (w); } @@ -1632,6 +1589,7 @@ { STAILQ_INSERT_HEAD(&w_free, w, w_list); + w_free_cnt++; } static struct witness_child_list_entry * @@ -1651,6 +1609,7 @@ return (NULL); } w_child_free = wcl->wcl_next; + w_child_free_cnt--; bzero(wcl, sizeof(*wcl)); return (wcl); } @@ -1661,6 +1620,7 @@ wcl->wcl_next = w_child_free; w_child_free = wcl; + w_child_free_cnt++; } static struct lock_list_entry *
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200601051922.k05JMGMc015613>