summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrigoriy Orlov <gluk@cvs.openbsd.org>2001-06-20 23:23:15 +0000
committerGrigoriy Orlov <gluk@cvs.openbsd.org>2001-06-20 23:23:15 +0000
commitbf3dda9a4a215ab111a7688acf2488b033d2633f (patch)
treea76e2cfbbc30907c6aba85128eca85c968098ddb
parentd56eede64774d87e208c1a59f08b1e556c8d5202 (diff)
Fix PR1826. tsleep in lf_setlock can return 0 if process was ptrace'd,
but not wakeup'ed. In this case one entry can be placed twice at list of blocked locks. As a result block list contain an entry which points to itself. lf_wakelock can't remove such an entry and system livelocks trying to remove a bad entry from block list. Other changes include: - repair debug functions and make vfs_lockf.c compilable with LOCKF_DEBUG - simplify debug printing and remove useless debugging - remove unnecessary casting, replace NOLOCKF with NULL - free -> FREE (use one form over the file) - convert list of blocked locks to use TAILQ_* macroses - lf_addblock() -> TAILQ_INSERT_TAIL - Fix bug in lf_findoverlap(): in old code if (end == -1) && (lf->lf_end == -1) && (lf->lf_start <= start) then lf_findoverlap() return 4 instead of 2 - work more carefully with pointers (probably fix one or two bugs) - use wakeup_one() - KNF
-rw-r--r--sys/kern/vfs_lockf.c302
-rw-r--r--sys/sys/lockf.h8
2 files changed, 130 insertions, 180 deletions
diff --git a/sys/kern/vfs_lockf.c b/sys/kern/vfs_lockf.c
index 62dc36807ac..7b35c1262b8 100644
--- a/sys/kern/vfs_lockf.c
+++ b/sys/kern/vfs_lockf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vfs_lockf.c,v 1.2 1996/03/03 17:20:26 niklas Exp $ */
+/* $OpenBSD: vfs_lockf.c,v 1.3 2001/06/20 23:23:14 gluk Exp $ */
/* $NetBSD: vfs_lockf.c,v 1.7 1996/02/04 02:18:21 christos Exp $ */
/*
@@ -55,14 +55,25 @@
*/
int maxlockdepth = MAXDEPTH;
-#ifdef LOCKF_DEBUG
-int lockf_debug = 0;
-#endif
-
-#define NOLOCKF (struct lockf *)0
#define SELF 0x1
#define OTHERS 0x2
+#ifdef LOCKF_DEBUG
+
+#define DEBUG_SETLOCK 0x01
+#define DEBUG_CLEARLOCK 0x02
+#define DEBUG_GETLOCK 0x04
+#define DEBUG_FINDOVR 0x08
+#define DEBUG_SPLIT 0x10
+#define DEBUG_WAKELOCK 0x20
+
+int lockf_debug = DEBUG_SETLOCK|DEBUG_CLEARLOCK|DEBUG_WAKELOCK;
+
+#define DPRINTF(args, level) if (lockf_debug & (level)) printf args
+#else
+#define DPRINTF(args, level)
+#endif
+
/*
* Do an advisory lock operation.
*/
@@ -82,7 +93,7 @@ lf_advlock(head, size, id, op, fl, flags)
/*
* Avoid the common case of unlocking when inode has no locks.
*/
- if (*head == (struct lockf *)0) {
+ if (*head == NULL) {
if (op != F_SETLK) {
fl->l_type = F_UNLCK;
return (0);
@@ -115,6 +126,7 @@ lf_advlock(head, size, id, op, fl, flags)
end = -1;
else
end = start + fl->l_len - 1;
+
/*
* Create the lockf structure.
*/
@@ -124,8 +136,8 @@ lf_advlock(head, size, id, op, fl, flags)
lock->lf_id = id;
lock->lf_head = head;
lock->lf_type = fl->l_type;
- lock->lf_next = (struct lockf *)0;
- lock->lf_block = (struct lockf *)0;
+ lock->lf_next = NULL;
+ TAILQ_INIT(&lock->lf_blkhd);
lock->lf_flags = flags;
/*
* Do the requested operation.
@@ -166,7 +178,7 @@ lf_setlock(lock)
int ovcase, priority, needtolink, error;
#ifdef LOCKF_DEBUG
- if (lockf_debug & 1)
+ if (lockf_debug & DEBUG_SETLOCK)
lf_print("lf_setlock", lock);
#endif /* LOCKF_DEBUG */
@@ -207,8 +219,8 @@ lf_setlock(lock)
/* The block is waiting on something */
wproc = (struct proc *)block->lf_id;
while (wproc->p_wchan &&
- (wproc->p_wmesg == lockstr) &&
- (i++ < maxlockdepth)) {
+ (wproc->p_wmesg == lockstr) &&
+ (i++ < maxlockdepth)) {
waitblock = (struct lockf *)wproc->p_wchan;
/* Get the owner of the blocking lock */
waitblock = waitblock->lf_next;
@@ -216,7 +228,7 @@ lf_setlock(lock)
break;
wproc = (struct proc *)waitblock->lf_id;
if (wproc == (struct proc *)lock->lf_id) {
- free(lock, M_LOCKF);
+ FREE(lock, M_LOCKF);
return (EDEADLK);
}
}
@@ -237,36 +249,33 @@ lf_setlock(lock)
* Remember who blocked us (for deadlock detection).
*/
lock->lf_next = block;
- lf_addblock(block, lock);
#ifdef LOCKF_DEBUG
- if (lockf_debug & 1) {
+ if (lockf_debug & DEBUG_SETLOCK) {
+ lf_print("lf_setlock", lock);
lf_print("lf_setlock: blocking on", block);
- lf_printlist("lf_setlock", block);
}
#endif /* LOCKF_DEBUG */
+ TAILQ_INSERT_TAIL(&block->lf_blkhd, lock, lf_block);
error = tsleep((caddr_t)lock, priority, lockstr, 0);
+#if 0
if (error) {
/*
* Delete ourselves from the waiting to lock list.
*/
- for (block = lock->lf_next;
- block != NOLOCKF;
- block = block->lf_block) {
- if (block->lf_block != lock)
- continue;
- block->lf_block = block->lf_block->lf_block;
- break;
- }
- /*
- * If we did not find ourselves on the list, but
- * are still linked onto a lock list, then something
- * is very wrong.
- */
- if (block == NOLOCKF && lock->lf_next != NOLOCKF)
- panic("lf_setlock: lost lock");
- free(lock, M_LOCKF);
+ TAILQ_REMOVE(&lock->lf_next->lf_blkhd, lock, lf_block);
+ FREE(lock, M_LOCKF);
+ return (error);
+ }
+#else
+ if (lock->lf_next != NULL) {
+ TAILQ_REMOVE(&lock->lf_next->lf_blkhd, lock, lf_block);
+ lock->lf_next = NULL;
+ }
+ if (error) {
+ FREE(lock, M_LOCKF);
return (error);
}
+#endif
}
/*
* No blocks!! Add the lock. Note that we will
@@ -318,7 +327,7 @@ lf_setlock(lock)
* Check for common starting point and different types.
*/
if (overlap->lf_type == lock->lf_type) {
- free(lock, M_LOCKF);
+ FREE(lock, M_LOCKF);
lock = overlap; /* for debug output below */
break;
}
@@ -340,9 +349,14 @@ lf_setlock(lock)
overlap->lf_type == F_WRLCK) {
lf_wakelock(overlap);
} else {
- ltmp = lock->lf_block;
- lock->lf_block = overlap->lf_block;
- lf_addblock(lock, ltmp);
+ while ((ltmp =
+ TAILQ_FIRST(&overlap->lf_blkhd))) {
+ TAILQ_REMOVE(&overlap->lf_blkhd, ltmp,
+ lf_block);
+ ltmp->lf_next = lock;
+ TAILQ_INSERT_TAIL(&lock->lf_blkhd,
+ ltmp, lf_block);
+ }
}
/*
* Add the new lock if necessary and delete the overlap.
@@ -354,7 +368,7 @@ lf_setlock(lock)
needtolink = 0;
} else
*prev = overlap->lf_next;
- free(overlap, M_LOCKF);
+ FREE(overlap, M_LOCKF);
continue;
case 4: /* overlap starts before lock */
@@ -384,9 +398,8 @@ lf_setlock(lock)
break;
}
#ifdef LOCKF_DEBUG
- if (lockf_debug & 1) {
+ if (lockf_debug & DEBUG_SETLOCK) {
lf_print("lf_setlock: got the lock", lock);
- lf_printlist("lf_setlock", lock);
}
#endif /* LOCKF_DEBUG */
return (0);
@@ -399,24 +412,22 @@ lf_setlock(lock)
* and remove it (or shrink it), then wakeup anyone we can.
*/
int
-lf_clearlock(unlock)
- register struct lockf *unlock;
+lf_clearlock(lock)
+ register struct lockf *lock;
{
- struct lockf **head = unlock->lf_head;
+ struct lockf **head = lock->lf_head;
register struct lockf *lf = *head;
struct lockf *overlap, **prev;
int ovcase;
- if (lf == NOLOCKF)
+ if (lf == NULL)
return (0);
#ifdef LOCKF_DEBUG
- if (unlock->lf_type != F_UNLCK)
- panic("lf_clearlock: bad type");
- if (lockf_debug & 1)
- lf_print("lf_clearlock", unlock);
+ if (lockf_debug & DEBUG_CLEARLOCK)
+ lf_print("lf_clearlock", lock);
#endif /* LOCKF_DEBUG */
prev = head;
- while ((ovcase = lf_findoverlap(lf, unlock, SELF,
+ while ((ovcase = lf_findoverlap(lf, lock, SELF,
&prev, &overlap)) != 0) {
/*
* Wakeup the list of locks to be retried.
@@ -431,36 +442,32 @@ lf_clearlock(unlock)
break;
case 2: /* overlap contains lock: split it */
- if (overlap->lf_start == unlock->lf_start) {
- overlap->lf_start = unlock->lf_end + 1;
+ if (overlap->lf_start == lock->lf_start) {
+ overlap->lf_start = lock->lf_end + 1;
break;
}
- lf_split(overlap, unlock);
- overlap->lf_next = unlock->lf_next;
+ lf_split(overlap, lock);
+ overlap->lf_next = lock->lf_next;
break;
case 3: /* lock contains overlap */
*prev = overlap->lf_next;
lf = overlap->lf_next;
- free(overlap, M_LOCKF);
+ FREE(overlap, M_LOCKF);
continue;
case 4: /* overlap starts before lock */
- overlap->lf_end = unlock->lf_start - 1;
+ overlap->lf_end = lock->lf_start - 1;
prev = &overlap->lf_next;
lf = overlap->lf_next;
continue;
case 5: /* overlap ends after lock */
- overlap->lf_start = unlock->lf_end + 1;
+ overlap->lf_start = lock->lf_end + 1;
break;
}
break;
}
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 1)
- lf_printlist("lf_clearlock", unlock);
-#endif /* LOCKF_DEBUG */
return (0);
}
@@ -476,7 +483,7 @@ lf_getlock(lock, fl)
register struct lockf *block;
#ifdef LOCKF_DEBUG
- if (lockf_debug & 1)
+ if (lockf_debug & DEBUG_CLEARLOCK)
lf_print("lf_getlock", lock);
#endif /* LOCKF_DEBUG */
@@ -506,12 +513,11 @@ struct lockf *
lf_getblock(lock)
register struct lockf *lock;
{
- struct lockf **prev, *overlap, *lf = *(lock->lf_head);
- int ovcase;
+ struct lockf **prev, *overlap, *lf;
prev = lock->lf_head;
- while ((ovcase = lf_findoverlap(lf, lock, OTHERS,
- &prev, &overlap)) != 0) {
+ lf = *prev;
+ while (lf_findoverlap(lf, lock, OTHERS, &prev, &overlap) != 0) {
/*
* We've found an overlap, see if it blocks us
*/
@@ -523,7 +529,7 @@ lf_getblock(lock)
*/
lf = overlap->lf_next;
}
- return (NOLOCKF);
+ return (NULL);
}
/*
@@ -543,16 +549,15 @@ lf_findoverlap(lf, lock, type, prev, overlap)
{
off_t start, end;
- *overlap = lf;
- if (lf == NOLOCKF)
- return (0);
#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
+ if (lf && lockf_debug & DEBUG_FINDOVR)
lf_print("lf_findoverlap: looking for overlap in", lock);
#endif /* LOCKF_DEBUG */
+
+ *overlap = lf;
start = lock->lf_start;
end = lock->lf_end;
- while (lf != NOLOCKF) {
+ while (lf != NULL) {
if (((type & SELF) && lf->lf_id != lock->lf_id) ||
((type & OTHERS) && lf->lf_id == lock->lf_id)) {
*prev = &lf->lf_next;
@@ -560,7 +565,7 @@ lf_findoverlap(lf, lock, type, prev, overlap)
continue;
}
#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
+ if (lockf_debug & DEBUG_FINDOVR)
lf_print("\tchecking", lf);
#endif /* LOCKF_DEBUG */
/*
@@ -574,64 +579,48 @@ lf_findoverlap(lf, lock, type, prev, overlap)
* 4) overlap starts before lock
* 5) overlap ends after lock
*/
+
+ /* Case 0 */
if ((lf->lf_end != -1 && start > lf->lf_end) ||
(end != -1 && lf->lf_start > end)) {
- /* Case 0 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("no overlap\n");
-#endif /* LOCKF_DEBUG */
+ DPRINTF(("no overlap\n"), DEBUG_FINDOVR);
if ((type & SELF) && end != -1 && lf->lf_start > end)
return (0);
*prev = &lf->lf_next;
*overlap = lf = lf->lf_next;
continue;
}
+ /* Case 1 */
if ((lf->lf_start == start) && (lf->lf_end == end)) {
- /* Case 1 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("overlap == lock\n");
-#endif /* LOCKF_DEBUG */
+ DPRINTF(("overlap == lock\n"), DEBUG_FINDOVR);
return (1);
}
+ /* Case 2 */
if ((lf->lf_start <= start) &&
- (end != -1) &&
- ((lf->lf_end >= end) || (lf->lf_end == -1))) {
- /* Case 2 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("overlap contains lock\n");
-#endif /* LOCKF_DEBUG */
+ (lf->lf_end == -1 ||
+ (end != -1 && lf->lf_end >= end))) {
+ DPRINTF(("overlap contains lock\n"), DEBUG_FINDOVR);
return (2);
}
+ /* Case 3 */
if (start <= lf->lf_start &&
- (end == -1 ||
- (lf->lf_end != -1 && end >= lf->lf_end))) {
- /* Case 3 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("lock contains overlap\n");
-#endif /* LOCKF_DEBUG */
+ (end == -1 ||
+ (lf->lf_end != -1 && end >= lf->lf_end))) {
+ DPRINTF(("lock contains overlap\n"), DEBUG_FINDOVR);
return (3);
}
+ /* Case 4 */
if ((lf->lf_start < start) &&
- ((lf->lf_end >= start) || (lf->lf_end == -1))) {
- /* Case 4 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("overlap starts before lock\n");
-#endif /* LOCKF_DEBUG */
+ ((lf->lf_end >= start) || (lf->lf_end == -1))) {
+ DPRINTF(("overlap starts before lock\n"),
+ DEBUG_FINDOVR);
return (4);
}
+ /* Case 5 */
if ((lf->lf_start > start) &&
- (end != -1) &&
- ((lf->lf_end > end) || (lf->lf_end == -1))) {
- /* Case 5 */
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- printf("overlap ends after lock\n");
-#endif /* LOCKF_DEBUG */
+ (end != -1) &&
+ ((lf->lf_end > end) || (lf->lf_end == -1))) {
+ DPRINTF(("overlap ends after lock\n"), DEBUG_FINDOVR);
return (5);
}
panic("lf_findoverlap: default");
@@ -640,34 +629,6 @@ lf_findoverlap(lf, lock, type, prev, overlap)
}
/*
- * Add a lock to the end of the blocked list.
- */
-void
-lf_addblock(lock, blocked)
- struct lockf *lock;
- struct lockf *blocked;
-{
- register struct lockf *lf;
-
- if (blocked == NOLOCKF)
- return;
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2) {
- lf_print("addblock: adding", blocked);
- lf_print("to blocked list of", lock);
- }
-#endif /* LOCKF_DEBUG */
- if ((lf = lock->lf_block) == NOLOCKF) {
- lock->lf_block = blocked;
- return;
- }
- while (lf->lf_block != NOLOCKF)
- lf = lf->lf_block;
- lf->lf_block = blocked;
- return;
-}
-
-/*
* Split a lock and a contained region into
* two or three locks as necessary.
*/
@@ -679,7 +640,7 @@ lf_split(lock1, lock2)
register struct lockf *splitlock;
#ifdef LOCKF_DEBUG
- if (lockf_debug & 2) {
+ if (lockf_debug & DEBUG_SPLIT) {
lf_print("lf_split", lock1);
lf_print("splitting from", lock2);
}
@@ -703,14 +664,14 @@ lf_split(lock1, lock2)
* the encompassing lock
*/
MALLOC(splitlock, struct lockf *, sizeof *splitlock, M_LOCKF, M_WAITOK);
- bcopy((caddr_t)lock1, (caddr_t)splitlock, sizeof *splitlock);
+ memcpy((caddr_t)splitlock, (caddr_t)lock1, sizeof (*splitlock));
splitlock->lf_start = lock2->lf_end + 1;
- splitlock->lf_block = NOLOCKF;
+ splitlock->lf_block.tqe_next = NULL;
+ TAILQ_INIT(&splitlock->lf_blkhd);
lock1->lf_end = lock2->lf_start - 1;
/*
* OK, now link it in
*/
- splitlock->lf_next = lock1->lf_next;
lock2->lf_next = splitlock;
lock1->lf_next = lock2;
}
@@ -719,23 +680,15 @@ lf_split(lock1, lock2)
* Wakeup a blocklist
*/
void
-lf_wakelock(listhead)
- struct lockf *listhead;
+lf_wakelock(lock)
+ struct lockf *lock;
{
- register struct lockf *blocklist, *wakelock;
-
- blocklist = listhead->lf_block;
- listhead->lf_block = NOLOCKF;
- while (blocklist != NOLOCKF) {
- wakelock = blocklist;
- blocklist = blocklist->lf_block;
- wakelock->lf_block = NOLOCKF;
- wakelock->lf_next = NOLOCKF;
-#ifdef LOCKF_DEBUG
- if (lockf_debug & 2)
- lf_print("lf_wakelock: awakening", wakelock);
-#endif /* LOCKF_DEBUG */
- wakeup((caddr_t)wakelock);
+ struct lockf *wakelock;
+
+ while ((wakelock = TAILQ_FIRST(&lock->lf_blkhd))) {
+ TAILQ_REMOVE(&lock->lf_blkhd, wakelock, lf_block);
+ wakelock->lf_next = NULL;
+ wakeup_one(wakelock);
}
}
@@ -748,24 +701,25 @@ lf_print(tag, lock)
char *tag;
register struct lockf *lock;
{
+ struct lockf *block;
printf("%s: lock %p for ", tag, lock);
if (lock->lf_flags & F_POSIX)
printf("proc %d", ((struct proc *)(lock->lf_id))->p_pid);
else
- printf("id 0x%x", lock->lf_id);
- printf(" in ino %d on dev <%d, %d>, %s, start %d, end %d",
- lock->lf_inode->i_number,
- major(lock->lf_inode->i_dev),
- minor(lock->lf_inode->i_dev),
+ printf("id %p", lock->lf_id);
+ printf(" %s, start %qx, end %qx",
lock->lf_type == F_RDLCK ? "shared" :
lock->lf_type == F_WRLCK ? "exclusive" :
lock->lf_type == F_UNLCK ? "unlock" :
"unknown", lock->lf_start, lock->lf_end);
- if (lock->lf_block)
- printf(" block %p\n", lock->lf_block);
- else
- printf("\n");
+ block = TAILQ_FIRST(&lock->lf_blkhd);
+ if (block)
+ printf(" block");
+ TAILQ_FOREACH(block, &lock->lf_blkhd, lf_block)
+ printf(" %p,", block);
+ printf("\n");
+
}
void
@@ -775,25 +729,19 @@ lf_printlist(tag, lock)
{
register struct lockf *lf;
- printf("%s: Lock list for ino %d on dev <%d, %d>:\n",
- tag, lock->lf_inode->i_number,
- major(lock->lf_inode->i_dev),
- minor(lock->lf_inode->i_dev));
- for (lf = lock->lf_inode->i_lockf; lf; lf = lf->lf_next) {
+ printf("%s: Lock list:\n", tag);
+ for (lf = *lock->lf_head; lf; lf = lf->lf_next) {
printf("\tlock %p for ", lf);
if (lf->lf_flags & F_POSIX)
- printf("proc %d", ((struct proc *)(lf->lf_id))->p_pid);
+ printf("proc %d", ((struct proc*)(lf->lf_id))->p_pid);
else
- printf("id 0x%x", lf->lf_id);
- printf(", %s, start %d, end %d",
+ printf("id %p", lf->lf_id);
+ printf(" %s, start %qx, end %qx",
lf->lf_type == F_RDLCK ? "shared" :
lf->lf_type == F_WRLCK ? "exclusive" :
lf->lf_type == F_UNLCK ? "unlock" :
"unknown", lf->lf_start, lf->lf_end);
- if (lf->lf_block)
- printf(" block %p\n", lf->lf_block);
- else
- printf("\n");
+ printf("\n");
}
}
#endif /* LOCKF_DEBUG */
diff --git a/sys/sys/lockf.h b/sys/sys/lockf.h
index 2b19b8aaeb3..3ccad9eb8f5 100644
--- a/sys/sys/lockf.h
+++ b/sys/sys/lockf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: lockf.h,v 1.2 1996/03/03 12:11:57 niklas Exp $ */
+/* $OpenBSD: lockf.h,v 1.3 2001/06/20 23:23:14 gluk Exp $ */
/* $NetBSD: lockf.h,v 1.5 1994/06/29 06:44:33 cgd Exp $ */
/*
@@ -45,6 +45,8 @@
* the inode structure. Locks are sorted by the starting byte of the lock for
* efficiency.
*/
+TAILQ_HEAD(locklist, lockf);
+
struct lockf {
short lf_flags; /* Lock semantics: F_POSIX, F_FLOCK, F_WAIT */
short lf_type; /* Lock type: F_RDLCK, F_WRLCK */
@@ -53,14 +55,14 @@ struct lockf {
caddr_t lf_id; /* The id of the resource holding the lock */
struct lockf **lf_head; /* Back pointer to the head of lockf list */
struct lockf *lf_next; /* A pointer to the next lock on this inode */
- struct lockf *lf_block; /* The list of blocked locks */
+ struct locklist lf_blkhd; /* The list of blocked locks */
+ TAILQ_ENTRY(lockf) lf_block; /* A request waiting for a lock */
};
/* Maximum length of sleep chains to traverse to try and detect deadlock. */
#define MAXDEPTH 50
__BEGIN_DECLS
-void lf_addblock __P((struct lockf *, struct lockf *));
int lf_advlock __P((struct lockf **,
off_t, caddr_t, int, struct flock *, int));
int lf_clearlock __P((struct lockf *));