Date: Thu, 9 Apr 1998 17:40:57 -0700 (PDT) From: Matt Dillon <dillon@best.net> To: FreeBSD-gnats-submit@FreeBSD.ORG Subject: kern/6258: FreeBSD-2.2.6 VM lockup on kernel map due to brelse calling bfreekva(), dump lockup in getnewbuf() due to fragmented buffer_map Message-ID: <199804100040.RAA01574@flea.best.net>
next in thread | raw e-mail | index | archive | help
>Number: 6258
>Category: kern
>Synopsis: A fix required to prevent kernel lockups in brelse causes the dump program to lockup in 'newbuf'
>Confidential: no
>Severity: critical
>Priority: high
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Thu Apr 9 17:50:00 PDT 1998
>Last-Modified:
>Originator: Matt Dillon
>Organization:
Best Internet Communications, Inc.
>Release: FreeBSD 2.2.6-STABLE i386
>Environment:
Heavily loaded shell machine, PPro 200, 128MB of ram.
>Description:
Problem #1: Kernel locks up in kernel map due to brelse() calling
bfreekva() from a SCSI interrupt while kernel map is already locked
(there was another bugtrack on this problem). The fix for this was
to defer calling bfreekva().
Problem #2: Unfortunately, this appears to create a new problem. The
new problem is that since bfreekva() is not called when a buffer
is released, the buffer_map can get fragmented and prevent large
getnewbuf allocations from succeeding.
The dump program attempts to allocate a 64K buffer to load the
disklabel. About once a week the program locks up in a 'newbuf'
waitstate, but still eats cpu because it is constantly being woken
up but then (as far as I can tell) is unable to allocate a bp of
sufficient size. This becomes a permanent condition.
Since we can't put the bfreekva() back into brelse(), my solution
is to put code in getnewbuf(). If the vm_map_findspace() call
fails, my proposed code (the second set of changes below) wipes
the kvm mappings for all EMPTY bp's in an attempt to defragment it
then retries the vm_map_findspace() call.
I'm running this code now but it hasn't hit it yet.
>How-To-Repeat:
I can't reliably get it repeatable. The problem happens once a week
or so on our admin machine. However, I believe the general problem
is important enough to be flagged critical since apparently the
original #ifdef notdef patch to remove bfreekva() did not make it
into 2.2.6. I don't know why. brelse() is a critical kernel call
that can occur in an interrupt and should not do anything complex...
certainly not call bfreekva(). Manipulating the kernel map and
associated insundry activity is much safer to do in getnewbuf() then
in brelse().
>Fix:
--- LINK/vfs_bio.c Fri Mar 13 13:13:57 1998
+++ vfs_bio.c Thu Apr 9 17:38:01 1998
@@ -597,10 +597,12 @@
LIST_REMOVE(bp, b_hash);
LIST_INSERT_HEAD(&invalhash, bp, b_hash);
bp->b_dev = NODEV;
+#ifdef notdef
/*
* Get rid of the kva allocation *now*
*/
bfreekva(bp);
+#endif
if (needsbuffer) {
wakeup(&needsbuffer);
needsbuffer=0;
@@ -986,9 +988,33 @@
*/
if (vm_map_findspace(buffer_map,
vm_map_min(buffer_map), maxsize, &addr)) {
- bp->b_flags |= B_INVAL;
- brelse(bp);
- goto trytofreespace;
+
+ /*
+ * Matt hack. Since we can't call bfreekva() in
+ * brelse(), the bp's on the EMPTY list may all
+ * still have allocated KVM. If we can't find
+ * unused space in the buffer_map, we should try
+ * to defragment the map by freeing as much from
+ * the empty list as possible.
+ */
+ printf("vm_map_findspace() failed, defragmenting freelist\n");
+ for (bp = TAILQ_FIRST(&bufqueues[QUEUE_EMPTY]);
+ bp;
+ bp = TAILQ_NEXT(bp, b_freelist)
+ ) {
+ if (bp->b_kvasize)
+ bfreekva(bp);
+ if (bp->b_qindex != QUEUE_EMPTY)
+ break;
+ }
+ addr = 0;
+ if (vm_map_findspace(buffer_map,
+ vm_map_min(buffer_map), maxsize, &addr)) {
+
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ goto trytofreespace;
+ }
}
}
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199804100040.RAA01574>
