- Move state about lock objects out of struct lock_object and into a new

struct lock_instance that is stored in the per-process and per-CPU lock
  lists.  Previously, the lock lists just kept a pointer to each lock held.
  That pointer is now replaced by a lock instance which contains a pointer
  to the lock object, the file and line of the last acquisition of a lock,
  and various flags about a lock including its recursion count.
- If we sleep while holding a sleepable lock, then mark that lock instance
  as having slept and ignore any lock order violations that occur while
  acquiring Giant when we wake up with slept locks.  This is ok because of
  Giant's special nature.
- Allow witness to differentiate between shared and exclusive locks and
  unlocks of a lock.  Witness will now detect the case when a lock is
  acquired first in one mode and then in another.  Mutexes are always
  locked and unlocked exclusively.  Witness will also now detect the case
  where a process attempts to unlock a shared lock while holding an
  exclusive lock and vice versa.
- Fix a bug in the lock list implementation where we used the wrong
  constant to detect the case where a lock list entry was full.
This commit is contained in:
John Baldwin 2001-05-04 17:15:16 +00:00
parent ac07d659c3
commit 2d96f0b145
7 changed files with 234 additions and 225 deletions

View File

@ -274,8 +274,8 @@ _mtx_trylock(struct mtx *m, int opts, const char *file, int line)
*/
KASSERT(!mtx_recursed(m),
("mtx_trylock() called on a recursed mutex"));
mtx_update_flags(m, 1);
WITNESS_LOCK(&m->mtx_object, opts | LOP_TRYLOCK, file, line);
WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE | LOP_TRYLOCK,
file, line);
}
return (rval);
@ -552,40 +552,6 @@ _mtx_unlock_sleep(struct mtx *m, int opts, const char *file, int line)
* See the _rel_spin_lock() macro for the details.
*/
#ifdef WITNESS
/*
* Update the lock object flags before calling witness. Note that when we
* lock a mutex, this is called after getting the lock, but when unlocking
* a mutex, this function is called before releasing the lock.
*/
void
_mtx_update_flags(struct mtx *m, int locking)
{
mtx_assert(m, MA_OWNED);
if (locking) {
m->mtx_object.lo_flags |= LO_LOCKED;
if (mtx_recursed(m))
m->mtx_object.lo_flags |= LO_RECURSED;
else
/* XXX: we shouldn't need this in theory. */
m->mtx_object.lo_flags &= ~LO_RECURSED;
} else {
switch (m->mtx_recurse) {
case 0:
/* XXX: we shouldn't need the LO_RECURSED in theory. */
m->mtx_object.lo_flags &= ~(LO_LOCKED | LO_RECURSED);
break;
case 1:
m->mtx_object.lo_flags &= ~(LO_RECURSED);
break;
default:
break;
}
}
}
#endif
/*
* The backing function for the INVARIANTS-enabled mtx_assert()
*/
@ -704,9 +670,8 @@ mtx_destroy(struct mtx *m)
MPASS((m->mtx_lock & (MTX_RECURSED|MTX_CONTESTED)) == 0);
/* Tell witness this isn't locked to make it happy. */
m->mtx_object.lo_flags &= ~LO_LOCKED;
WITNESS_UNLOCK(&m->mtx_object, MTX_NOSWITCH, __FILE__,
__LINE__);
WITNESS_UNLOCK(&m->mtx_object, LOP_EXCLUSIVE | LOP_NOSWITCH,
__FILE__, __LINE__);
}
WITNESS_DESTROY(&m->mtx_object);

View File

@ -33,13 +33,6 @@
*
* Priority propagation will not generally raise the priority of lock holders,
* so should not be relied upon in combination with sx locks.
*
* The witness code can not detect lock cycles (yet).
*
* XXX: When witness is made to function with sx locks, it will need to
* XXX: be taught to deal with these situations, as they are more involved:
* slock --> xlock (deadlock)
* slock --> slock (slock recursion, not fatal)
*/
#include <sys/param.h>
@ -50,10 +43,6 @@
#include <sys/mutex.h>
#include <sys/sx.h>
/*
* XXX: We don't implement the LO_RECURSED flag for this lock yet.
* We could do this by walking p_sleeplocks if we really wanted to.
*/
struct lock_class lock_class_sx = {
"sx",
LC_SLEEPLOCK | LC_SLEEPABLE | LC_RECURSABLE
@ -68,7 +57,7 @@ sx_init(struct sx *sx, const char *description)
lock = &sx->sx_object;
lock->lo_class = &lock_class_sx;
lock->lo_name = description;
lock->lo_flags = LO_WITNESS | LO_SLEEPABLE;
lock->lo_flags = LO_WITNESS | LO_RECURSABLE | LO_SLEEPABLE;
mtx_init(&sx->sx_lock, "sx backing lock",
MTX_DEF | MTX_NOWITNESS | MTX_QUIET);
sx->sx_cnt = 0;
@ -121,9 +110,6 @@ _sx_slock(struct sx *sx, const char *file, int line)
/* Acquire a shared lock. */
sx->sx_cnt++;
#ifdef WITNESS
sx->sx_object.lo_flags |= LO_LOCKED;
#endif
LOCK_LOG_LOCK("SLOCK", &sx->sx_object, 0, 0, file, line);
WITNESS_LOCK(&sx->sx_object, 0, file, line);
@ -160,11 +146,8 @@ _sx_xlock(struct sx *sx, const char *file, int line)
sx->sx_cnt--;
sx->sx_xholder = curproc;
#ifdef WITNESS
sx->sx_object.lo_flags |= LO_LOCKED;
#endif
LOCK_LOG_LOCK("XLOCK", &sx->sx_object, 0, 0, file, line);
WITNESS_LOCK(&sx->sx_object, 0, file, line);
WITNESS_LOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
mtx_unlock(&sx->sx_lock);
}
@ -176,10 +159,6 @@ _sx_sunlock(struct sx *sx, const char *file, int line)
mtx_lock(&sx->sx_lock);
_SX_ASSERT_SLOCKED(sx);
#ifdef WITNESS
if (sx->sx_cnt == 0)
sx->sx_object.lo_flags &= ~LO_LOCKED;
#endif
WITNESS_UNLOCK(&sx->sx_object, 0, file, line);
/* Release. */
@ -210,10 +189,7 @@ _sx_xunlock(struct sx *sx, const char *file, int line)
_SX_ASSERT_XLOCKED(sx);
MPASS(sx->sx_cnt == -1);
#ifdef WITNESS
sx->sx_object.lo_flags &= ~LO_LOCKED;
#endif
WITNESS_UNLOCK(&sx->sx_object, 0, file, line);
WITNESS_UNLOCK(&sx->sx_object, LOP_EXCLUSIVE, file, line);
/* Release. */
sx->sx_cnt++;

View File

@ -274,8 +274,8 @@ _mtx_trylock(struct mtx *m, int opts, const char *file, int line)
*/
KASSERT(!mtx_recursed(m),
("mtx_trylock() called on a recursed mutex"));
mtx_update_flags(m, 1);
WITNESS_LOCK(&m->mtx_object, opts | LOP_TRYLOCK, file, line);
WITNESS_LOCK(&m->mtx_object, opts | LOP_EXCLUSIVE | LOP_TRYLOCK,
file, line);
}
return (rval);
@ -552,40 +552,6 @@ _mtx_unlock_sleep(struct mtx *m, int opts, const char *file, int line)
* See the _rel_spin_lock() macro for the details.
*/
#ifdef WITNESS
/*
* Update the lock object flags before calling witness. Note that when we
* lock a mutex, this is called after getting the lock, but when unlocking
* a mutex, this function is called before releasing the lock.
*/
void
_mtx_update_flags(struct mtx *m, int locking)
{
mtx_assert(m, MA_OWNED);
if (locking) {
m->mtx_object.lo_flags |= LO_LOCKED;
if (mtx_recursed(m))
m->mtx_object.lo_flags |= LO_RECURSED;
else
/* XXX: we shouldn't need this in theory. */
m->mtx_object.lo_flags &= ~LO_RECURSED;
} else {
switch (m->mtx_recurse) {
case 0:
/* XXX: we shouldn't need the LO_RECURSED in theory. */
m->mtx_object.lo_flags &= ~(LO_LOCKED | LO_RECURSED);
break;
case 1:
m->mtx_object.lo_flags &= ~(LO_RECURSED);
break;
default:
break;
}
}
}
#endif
/*
* The backing function for the INVARIANTS-enabled mtx_assert()
*/
@ -704,9 +670,8 @@ mtx_destroy(struct mtx *m)
MPASS((m->mtx_lock & (MTX_RECURSED|MTX_CONTESTED)) == 0);
/* Tell witness this isn't locked to make it happy. */
m->mtx_object.lo_flags &= ~LO_LOCKED;
WITNESS_UNLOCK(&m->mtx_object, MTX_NOSWITCH, __FILE__,
__LINE__);
WITNESS_UNLOCK(&m->mtx_object, LOP_EXCLUSIVE | LOP_NOSWITCH,
__FILE__, __LINE__);
}
WITNESS_DESTROY(&m->mtx_object);

View File

@ -139,6 +139,8 @@ static void witness_child_free(struct witness_child_list_entry *wcl);
static struct lock_list_entry *witness_lock_list_get(void);
static void witness_lock_list_free(struct lock_list_entry *lle);
static void witness_display(void(*)(const char *fmt, ...));
static struct lock_instance *find_instance(struct lock_list_entry *lock_list,
struct lock_object *lock);
MALLOC_DEFINE(M_WITNESS, "witness", "witness structure");
@ -245,8 +247,6 @@ STAILQ_HEAD(, lock_object) all_locks = STAILQ_HEAD_INITIALIZER(all_locks);
static struct mtx all_mtx = {
{ &lock_class_mtx_sleep, /* mtx_object.lo_class */
"All locks list", /* mtx_object.lo_name */
NULL, /* mtx_object.lo_file */
0, /* mtx_object.lo_line */
LO_INITIALIZED, /* mtx_object.lo_flags */
{ NULL }, /* mtx_object.lo_list */
NULL }, /* mtx_object.lo_witness */
@ -342,12 +342,12 @@ witness_init(struct lock_object *lock)
(class->lc_flags & LC_RECURSABLE) == 0)
panic("%s: lock (%s) %s can not be recursable!\n", __func__,
class->lc_name, lock->lo_name);
if ((lock->lo_flags & LO_SLEEPABLE) != 0 &&
(class->lc_flags & LC_SLEEPABLE) == 0)
panic("%s: lock (%s) %s can not be sleepable!\n", __func__,
class->lc_name, lock->lo_name);
mtx_lock(&all_mtx);
STAILQ_INSERT_TAIL(&all_locks, lock, lo_list);
lock->lo_flags |= LO_INITIALIZED;
@ -375,10 +375,7 @@ witness_destroy(struct lock_object *lock)
panic("%s: lock (%s) %s is not initialized!\n", __func__,
lock->lo_class->lc_name, lock->lo_name);
if (lock->lo_flags & LO_LOCKED)
panic("lock (%s) %s destroyed while held",
lock->lo_class->lc_name, lock->lo_name);
/* XXX: need to verify that no one holds the lock */
w = lock->lo_witness;
if (w != NULL) {
mtx_lock_spin(&w_mtx);
@ -460,7 +457,7 @@ void
witness_lock(struct lock_object *lock, int flags, const char *file, int line)
{
struct lock_list_entry **lock_list, *lle;
struct lock_object *lock1, *lock2;
struct lock_instance *lock1, *lock2;
struct lock_class *class;
struct witness *w, *w1;
struct proc *p;
@ -476,19 +473,6 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
class = lock->lo_class;
p = curproc;
if ((lock->lo_flags & LO_LOCKED) == 0)
panic("%s: lock (%s) %s is not locked @ %s:%d", __func__,
class->lc_name, lock->lo_name, file, line);
if ((lock->lo_flags & LO_RECURSED) != 0) {
if ((lock->lo_flags & LO_RECURSABLE) == 0)
panic(
"%s: recursed on non-recursive lock (%s) %s @ %s:%d first aquired @ %s:%d",
__func__, class->lc_name, lock->lo_name, file,
line, lock->lo_file, lock->lo_line);
return;
}
/*
* We have to hold a spinlock to keep lock_list valid across the check
* in the LC_SLEEPLOCK case. In the LC_SPINLOCK case, it is already
@ -520,20 +504,55 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
if (*lock_list == NULL)
goto out;
/*
* Check to see if we are recursing on a lock we already own.
*/
lock1 = find_instance(*lock_list, lock);
if (lock1 != NULL) {
if ((lock1->li_flags & LI_EXCLUSIVE) != 0 &&
(flags & LOP_EXCLUSIVE) == 0) {
printf("shared lock of (%s) %s @ %s:%d\n",
class->lc_name, lock->lo_name, file, line);
printf("while exclusively locked from %s:%d\n",
lock1->li_file, lock1->li_line);
panic("share->excl");
}
if ((lock1->li_flags & LI_EXCLUSIVE) == 0 &&
(flags & LOP_EXCLUSIVE) != 0) {
printf("exclusive lock of (%s) %s @ %s:%d\n",
class->lc_name, lock->lo_name, file, line);
printf("while share locked from %s:%d\n",
lock1->li_file, lock1->li_line);
panic("excl->share");
}
lock1->li_flags++;
if ((lock->lo_flags & LO_RECURSABLE) == 0) {
printf(
"recursed on non-recursive lock (%s) %s @ %s:%d\n",
class->lc_name, lock->lo_name, file, line);
printf("first acquired @ %s:%d\n", lock1->li_file,
lock1->li_line);
panic("recurse");
}
lock1->li_file = file;
lock1->li_line = line;
return;
}
/*
* Check for duplicate locks of the same type. Note that we only
* have to check for this on the last lock we just acquired. Any
* other cases will be caught as lock order violations.
*/
lock1 = (*lock_list)->ll_children[(*lock_list)->ll_count - 1];
w1 = lock1->lo_witness;
lock1 = &(*lock_list)->ll_children[(*lock_list)->ll_count - 1];
w1 = lock1->li_lock->lo_witness;
if (w1 == w) {
if (w->w_same_squawked || dup_ok(w))
goto out;
w->w_same_squawked = 1;
printf("acquiring duplicate lock of same type: \"%s\"\n",
lock->lo_name);
printf(" 1st @ %s:%d\n", w->w_file, w->w_line);
printf(" 1st @ %s:%d\n", lock1->li_file, lock1->li_line);
printf(" 2nd @ %s:%d\n", file, line);
#ifdef DDB
go_into_ddb = 1;
@ -557,18 +576,25 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
for (i = lle->ll_count - 1; i >= 0; i--, j++) {
MPASS(j < WITNESS_COUNT);
lock1 = lle->ll_children[i];
w1 = lock1->lo_witness;
lock1 = &lle->ll_children[i];
w1 = lock1->li_lock->lo_witness;
/*
* If this lock doesn't undergo witness checking,
* then skip it.
*/
if (w1 == NULL) {
KASSERT((lock1->lo_flags & LO_WITNESS) == 0,
KASSERT((lock1->li_lock->lo_flags & LO_WITNESS) == 0,
("lock missing witness structure"));
continue;
}
/*
* If we are locking Giant and we slept with this
* lock, then skip it.
*/
if ((lock1->li_flags & LI_SLEPT) != 0 &&
lock == &Giant.mtx_object)
continue;
if (!isitmydescendant(w, w1))
continue;
/*
@ -578,7 +604,7 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
mtx_unlock_spin(&w_mtx);
if (blessed(w, w1))
goto out;
if (lock1 == &Giant.mtx_object) {
if (lock1->li_lock == &Giant.mtx_object) {
if (w1->w_Giant_squawked)
goto out;
else
@ -598,9 +624,9 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
* witness w in our list.
*/
do {
lock2 = lle->ll_children[i];
MPASS(lock2 != NULL);
if (lock2->lo_witness == w)
lock2 = &lle->ll_children[i];
MPASS(lock2->li_lock != NULL);
if (lock2->li_lock->lo_witness == w)
break;
i--;
if (i == 0 && lle->ll_next != NULL) {
@ -609,29 +635,30 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
MPASS(i != 0);
}
} while (i >= 0);
if (i < 0)
/*
* We are very likely bogus in this case.
*/
printf(" 1st %s last acquired @ %s:%d\n",
w->w_name, w->w_file, w->w_line);
else
printf(" 1st %p %s @ %s:%d\n", lock2,
lock2->lo_name, lock2->lo_file,
lock2->lo_line);
printf(" 2nd %p %s @ %s:%d\n",
lock1, lock1->lo_name, lock1->lo_file,
lock1->lo_line);
printf(" 3rd %p %s @ %s:%d\n",
lock, lock->lo_name, file, line);
if (i < 0) {
printf(" 1st %p %s @ %s:%d\n", lock1->li_lock,
lock1->li_lock->lo_name, lock1->li_file,
lock1->li_line);
printf(" 2nd %p %s @ %s:%d\n", lock,
lock->lo_name, file, line);
} else {
printf(" 1st %p %s @ %s:%d\n", lock2->li_lock,
lock2->li_lock->lo_name, lock2->li_file,
lock2->li_line);
printf(" 2nd %p %s @ %s:%d\n", lock1->li_lock,
lock1->li_lock->lo_name, lock1->li_file,
lock1->li_line);
printf(" 3rd %p %s @ %s:%d\n", lock,
lock->lo_name, file, line);
}
#ifdef DDB
go_into_ddb = 1;
#endif /* DDB */
goto out;
}
}
lock1 = (*lock_list)->ll_children[(*lock_list)->ll_count - 1];
if (!itismychild(lock1->lo_witness, w))
lock1 = &(*lock_list)->ll_children[(*lock_list)->ll_count - 1];
if (!itismychild(lock1->li_lock->lo_witness, w))
mtx_unlock_spin(&w_mtx);
out:
@ -641,24 +668,30 @@ witness_lock(struct lock_object *lock, int flags, const char *file, int line)
#endif /* DDB */
w->w_file = file;
w->w_line = line;
lock->lo_line = line;
lock->lo_file = file;
lle = *lock_list;
if (lle == NULL || lle->ll_count == LOCK_CHILDCOUNT) {
if (lle == NULL || lle->ll_count == LOCK_NCHILDREN) {
*lock_list = witness_lock_list_get();
if (*lock_list == NULL)
return;
(*lock_list)->ll_next = lle;
lle = *lock_list;
}
lle->ll_children[lle->ll_count++] = lock;
lock1 = &lle->ll_children[lle->ll_count++];
lock1->li_lock = lock;
lock1->li_line = line;
lock1->li_file = file;
if ((flags & LOP_EXCLUSIVE) != 0)
lock1->li_flags = LI_EXCLUSIVE;
else
lock1->li_flags = 0;
}
void
witness_unlock(struct lock_object *lock, int flags, const char *file, int line)
{
struct lock_list_entry **lock_list, *lle;
struct lock_instance *instance;
struct lock_class *class;
struct proc *p;
int i, j;
@ -668,15 +701,58 @@ witness_unlock(struct lock_object *lock, int flags, const char *file, int line)
return;
p = curproc;
class = lock->lo_class;
if (lock->lo_flags & LO_RECURSED) {
if ((lock->lo_flags & LO_LOCKED) == 0)
panic("%s: recursed lock (%s) %s is not locked @ %s:%d",
__func__, class->lc_name, lock->lo_name, file,
line);
return;
}
if (class->lc_flags & LC_SLEEPLOCK)
lock_list = &p->p_sleeplocks;
else
lock_list = PCPU_PTR(spinlocks);
for (; *lock_list != NULL; lock_list = &(*lock_list)->ll_next)
for (i = 0; i < (*lock_list)->ll_count; i++) {
instance = &(*lock_list)->ll_children[i];
if (instance->li_lock == lock) {
if ((instance->li_flags & LI_EXCLUSIVE) != 0 &&
(flags & LOP_EXCLUSIVE) == 0) {
printf(
"shared unlock of (%s) %s @ %s:%d\n",
class->lc_name, lock->lo_name,
file, line);
printf(
"while exclusively locked from %s:%d\n",
instance->li_file,
instance->li_line);
panic("excl->ushare");
}
if ((instance->li_flags & LI_EXCLUSIVE) == 0 &&
(flags & LOP_EXCLUSIVE) != 0) {
printf(
"exclusive unlock of (%s) %s @ %s:%d\n",
class->lc_name, lock->lo_name,
file, line);
printf(
"while share locked from %s:%d\n",
instance->li_file,
instance->li_line);
panic("share->uexcl");
}
/* If we are recursed, unrecurse. */
if ((instance->li_flags & LI_RECURSEMASK) > 0) {
instance->li_flags--;
goto out;
}
(*lock_list)->ll_count--;
for (j = i; j < (*lock_list)->ll_count; j++)
(*lock_list)->ll_children[j] =
(*lock_list)->ll_children[j + 1];
if ((*lock_list)->ll_count == 0) {
lle = *lock_list;
*lock_list = lle->ll_next;
witness_lock_list_free(lle);
}
goto out;
}
}
panic("lock (%s) %s not locked @ %s:%d", class->lc_name, lock->lo_name,
file, line);
out:
/*
* We don't need to protect this PCPU_GET() here against preemption
* because if we hold any spinlocks then we are already protected,
@ -687,24 +763,7 @@ witness_unlock(struct lock_object *lock, int flags, const char *file, int line)
if ((flags & LOP_NOSWITCH) == 0 && PCPU_GET(spinlocks) != NULL)
panic("switchable sleep unlock (%s) %s @ %s:%d",
class->lc_name, lock->lo_name, file, line);
lock_list = &p->p_sleeplocks;
} else
lock_list = PCPU_PTR(spinlocks);
for (; *lock_list != NULL; lock_list = &(*lock_list)->ll_next)
for (i = 0; i < (*lock_list)->ll_count; i++)
if ((*lock_list)->ll_children[i] == lock) {
(*lock_list)->ll_count--;
for (j = i; j < (*lock_list)->ll_count; j++)
(*lock_list)->ll_children[j] =
(*lock_list)->ll_children[j + 1];
if ((*lock_list)->ll_count == 0) {
lle = *lock_list;
*lock_list = lle->ll_next;
witness_lock_list_free(lle);
}
return;
}
}
}
/*
@ -717,7 +776,7 @@ witness_sleep(int check_only, struct lock_object *lock, const char *file,
int line)
{
struct lock_list_entry **lock_list, *lle;
struct lock_object *lock1;
struct lock_instance *lock1;
struct proc *p;
critical_t savecrit;
int i, n;
@ -735,14 +794,20 @@ witness_sleep(int check_only, struct lock_object *lock, const char *file,
again:
for (lle = *lock_list; lle != NULL; lle = lle->ll_next)
for (i = lle->ll_count - 1; i >= 0; i--) {
lock1 = lle->ll_children[i];
if (lock1 == lock || lock1 == &Giant.mtx_object ||
(lock1->lo_flags & LO_SLEEPABLE))
lock1 = &lle->ll_children[i];
if (lock1->li_lock == lock ||
lock1->li_lock == &Giant.mtx_object)
continue;
if ((lock1->li_lock->lo_flags & LO_SLEEPABLE) != 0) {
if (check_only == 0)
lock1->li_flags |= LI_SLEPT;
continue;
}
n++;
printf("%s:%d: %s with \"%s\" locked from %s:%d\n",
file, line, check_only ? "could sleep" : "sleeping",
lock1->lo_name, lock1->lo_file, lock1->lo_line);
lock1->li_lock->lo_name, lock1->li_file,
lock1->li_line);
}
if (lock_list == &p->p_sleeplocks) {
lock_list = PCPU_PTR(spinlocks);
@ -1105,20 +1170,40 @@ witness_lock_list_free(struct lock_list_entry *lle)
mtx_unlock_spin(&w_mtx);
}
static struct lock_instance *
find_instance(struct lock_list_entry *lock_list, struct lock_object *lock)
{
struct lock_list_entry *lle;
struct lock_instance *instance;
int i;
for (lle = lock_list; lle != NULL; lle = lle->ll_next)
for (i = lle->ll_count - 1; i >= 0; i--) {
instance = &lle->ll_children[i];
if (instance->li_lock == lock)
return (instance);
}
return (NULL);
}
int
witness_list_locks(struct lock_list_entry **lock_list)
{
struct lock_list_entry *lle;
struct lock_instance *instance;
struct lock_object *lock;
int i, nheld;
nheld = 0;
for (lle = *lock_list; lle != NULL; lle = lle->ll_next)
for (i = lle->ll_count - 1; i >= 0; i--) {
lock = lle->ll_children[i];
printf("\t(%s) %s (%p) locked at %s:%d\n",
instance = &lle->ll_children[i];
lock = instance->li_lock;
printf("%s (%s) %s (%p) locked @ %s:%d\n",
(instance->li_flags & LI_EXCLUSIVE) != 0 ?
"exclusive" : "shared",
lock->lo_class->lc_name, lock->lo_name, lock,
lock->lo_file, lock->lo_line);
instance->li_file, instance->li_line);
nheld++;
}
return (nheld);
@ -1162,27 +1247,43 @@ witness_list(struct proc *p)
void
witness_save(struct lock_object *lock, const char **filep, int *linep)
{
struct lock_instance *instance;
KASSERT(!witness_cold, ("%s: witness_cold\n", __func__));
if (lock->lo_witness == NULL)
return;
*filep = lock->lo_file;
*linep = lock->lo_line;
KASSERT(lock->lo_class->lc_flags & LC_SLEEPLOCK,
("%s: lock (%s) %s is not a sleep lock", __func__,
lock->lo_class->lc_name, lock->lo_name));
instance = find_instance(curproc->p_sleeplocks, lock);
KASSERT(instance != NULL, ("%s: lock (%s) %s not locked", __func__,
lock->lo_class->lc_name, lock->lo_name));
*filep = instance->li_file;
*linep = instance->li_line;
}
void
witness_restore(struct lock_object *lock, const char *file, int line)
{
struct lock_instance *instance;
KASSERT(!witness_cold, ("%s: witness_cold\n", __func__));
if (lock->lo_witness == NULL)
return;
KASSERT(lock->lo_class->lc_flags & LC_SLEEPLOCK,
("%s: lock (%s) %s is not a sleep lock", __func__,
lock->lo_class->lc_name, lock->lo_name));
instance = find_instance(curproc->p_sleeplocks, lock);
KASSERT(instance != NULL, ("%s: lock (%s) %s not locked", __func__,
lock->lo_class->lc_name, lock->lo_name));
lock->lo_witness->w_file = file;
lock->lo_witness->w_line = line;
lock->lo_file = file;
lock->lo_line = line;
instance->li_file = file;
instance->li_line = line;
}
#ifdef DDB

View File

@ -34,8 +34,6 @@
struct lock_object {
struct lock_class *lo_class;
const char *lo_name;
const char *lo_file; /* File and line of last acquire. */
int lo_line;
u_int lo_flags;
STAILQ_ENTRY(lock_object) lo_list; /* List of all locks in system. */
struct witness *lo_witness;

View File

@ -67,8 +67,10 @@ struct lock_class {
#define LO_QUIET 0x00040000 /* Don't log locking operations. */
#define LO_RECURSABLE 0x00080000 /* Lock may recurse. */
#define LO_SLEEPABLE 0x00100000 /* Lock may be held while sleeping. */
#define LO_LOCKED 0x01000000 /* Someone holds this lock. */
#define LO_RECURSED 0x02000000 /* Someone has recursed on this lock. */
#define LI_RECURSEMASK 0x0000ffff /* Recursion depth of lock instance. */
#define LI_SLEPT 0x00010000 /* Lock instance has been slept with. */
#define LI_EXCLUSIVE 0x00020000 /* Exclusive lock instance. */
/*
* Option flags passed to lock operations that witness also needs to know
@ -77,8 +79,22 @@ struct lock_class {
#define LOP_NOSWITCH 0x00000001 /* Lock doesn't switch on release. */
#define LOP_QUIET 0x00000002 /* Don't log locking operations. */
#define LOP_TRYLOCK 0x00000004 /* Don't check lock order. */
#define LOP_EXCLUSIVE 0x00000008 /* Exclusive lock. */
#ifdef _KERNEL
/*
* Lock instances. A lock instance is the data associated with a lock while
* it is held by witness. For example, a lock instance will hold the
* recursion count of a lock. Lock instances are held in lists. Spin locks
* are held in a per-cpu list while sleep locks are held in per-process list.
*/
struct lock_instance {
struct lock_object *li_lock;
const char *li_file; /* File and line of last acquire. */
int li_line;
u_int li_flags; /* Recursion count and LI_* flags. */
};
/*
* A simple list type used to build the list of locks held by a process
* or CPU. We can't simply embed the list in struct lock_object since a
@ -89,11 +105,11 @@ struct lock_class {
* when we traverse the list we read children[count-1] as the first entry
* down to children[0] as the final entry.
*/
#define LOCK_NCHILDREN 6
#define LOCK_NCHILDREN 3
struct lock_list_entry {
struct lock_list_entry *ll_next;
struct lock_object *ll_children[LOCK_NCHILDREN];
struct lock_instance ll_children[LOCK_NCHILDREN];
u_int ll_count;
};

View File

@ -115,9 +115,6 @@ void _mtx_unlock_spin_flags(struct mtx *m, int opts, const char *file,
#ifdef INVARIANT_SUPPORT
void _mtx_assert(struct mtx *m, int what, const char *file, int line);
#endif
#ifdef WITNESS
void _mtx_update_flags(struct mtx *m, int locking);
#endif
/*
* We define our machine-independent (unoptimized) mutex micro-operations
@ -204,15 +201,6 @@ void _mtx_update_flags(struct mtx *m, int locking);
} while (0)
#endif
/*
* Update the lock object flags based on the current mutex state.
*/
#ifdef WITNESS
#define mtx_update_flags(m, locking) _mtx_update_flags((m), (locking))
#else
#define mtx_update_flags(m, locking)
#endif
/*
* Exported lock manipulation interface.
*
@ -277,8 +265,8 @@ void _mtx_update_flags(struct mtx *m, int locking);
_get_sleep_lock((m), curproc, (opts), (file), (line)); \
LOCK_LOG_LOCK("LOCK", &(m)->mtx_object, opts, m->mtx_recurse, \
(file), (line)); \
mtx_update_flags((m), 1); \
WITNESS_LOCK(&(m)->mtx_object, (opts), (file), (line)); \
WITNESS_LOCK(&(m)->mtx_object, (opts) | LOP_EXCLUSIVE, (file), \
(line)); \
} while (0)
#define __mtx_lock_spin_flags(m, opts, file, line) do { \
@ -286,28 +274,28 @@ void _mtx_update_flags(struct mtx *m, int locking);
_get_spin_lock((m), curproc, (opts), (file), (line)); \
LOCK_LOG_LOCK("LOCK", &(m)->mtx_object, opts, m->mtx_recurse, \
(file), (line)); \
mtx_update_flags((m), 1); \
WITNESS_LOCK(&(m)->mtx_object, (opts), (file), (line)); \
WITNESS_LOCK(&(m)->mtx_object, (opts) | LOP_EXCLUSIVE, (file), \
(line)); \
} while (0)
#define __mtx_unlock_flags(m, opts, file, line) do { \
MPASS(curproc != NULL); \
mtx_assert((m), MA_OWNED); \
mtx_update_flags((m), 0); \
WITNESS_UNLOCK(&(m)->mtx_object, (opts), (file), (line)); \
_rel_sleep_lock((m), curproc, (opts), (file), (line)); \
WITNESS_UNLOCK(&(m)->mtx_object, (opts) | LOP_EXCLUSIVE, \
(file), (line)); \
LOCK_LOG_LOCK("UNLOCK", &(m)->mtx_object, (opts), \
(m)->mtx_recurse, (file), (line)); \
_rel_sleep_lock((m), curproc, (opts), (file), (line)); \
} while (0)
#define __mtx_unlock_spin_flags(m, opts, file, line) do { \
MPASS(curproc != NULL); \
mtx_assert((m), MA_OWNED); \
mtx_update_flags((m), 0); \
WITNESS_UNLOCK(&(m)->mtx_object, (opts), (file), (line)); \
_rel_spin_lock((m)); \
WITNESS_UNLOCK(&(m)->mtx_object, (opts) | LOP_EXCLUSIVE, \
(file), (line)); \
LOCK_LOG_LOCK("UNLOCK", &(m)->mtx_object, (opts), \
(m)->mtx_recurse, (file), (line)); \
_rel_spin_lock((m)); \
} while (0)
#define mtx_trylock_flags(m, opts) \