diff options
author | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-06-20 23:23:15 +0000 |
---|---|---|
committer | Grigoriy Orlov <gluk@cvs.openbsd.org> | 2001-06-20 23:23:15 +0000 |
commit | bf3dda9a4a215ab111a7688acf2488b033d2633f (patch) | |
tree | a76e2cfbbc30907c6aba85128eca85c968098ddb | |
parent | d56eede64774d87e208c1a59f08b1e556c8d5202 (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.c | 302 | ||||
-rw-r--r-- | sys/sys/lockf.h | 8 |
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 *)); |