lkml.org 
[lkml]   [1999]   [Dec]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: Kernel panic: B_FREE inserted into queues
On Mon, 6 Dec 1999 stephan@a2000.nu wrote:

>yeah, but official raid code in kernel is raid 0.5 patches
>and the raid0 filesystem is on raid 0.9 ;-)

I checked the raid patch that you are using applyed over 2.2.14pre11 and
the problem is definitely there (as expected). See:

void set_blocksize(kdev_t dev, int size)
{
[..]
/*
* lets be mega-conservative about what to free:
*/
if (!(bh->b_dev != dev) &&
!(bh->b_size == size) &&
!bh->b_count &&
!buffer_protected(bh) &&
!buffer_dirty(bh) &&
!buffer_locked(bh) &&
!waitqueue_active(&bh->b_wait)) {
remove_from_hash_queue(bh);
bh->b_dev = NODEV;
refile_buffer(bh);
try_to_free_buffers(buffer_page(bh));
} else {
remove_from_queues(bh);
bh->b_dev=B_FREE;
^^^^^^^^^^^^^^^^
insert_into_queues(bh);
^^^^^^^^^^^^^^^^^^^^^^
}
[..]
}

The first thing to note is that the above snapshot basically means:

/*
* lets be mega-conservative about what to free:
*/
if (1)
destroy_information();
else
destroy_information();

So basically in both cases the raid code is destroying the buffer data
(even if the buffer it's in-use!). So the above snapshot of code is very
buggy.

More in detail: these checks are not necessary:

if (!(bh->b_dev != dev) &&
!(bh->b_size == size) &&

because if b_dev != dev or b_size is != size we should panic and not
insert the buffer into the freelist. If the b_dev or b_size changed it
means the buffer head memory is been freed and reused from under us (mm
corruption going on).

We can drop also all protected buffers unconditionally. So the ramdisk is
allowed to have only one blocksize at once (so only the memory we have
selected will be allocated).

!buffer_protected(bh) &&

If the buffer is dirty as usual we shouldn't destroy it. But the point is
that there shouldn't be dirty buffers anymore in such path. It's not like
invalidate_buffers that is going to be called on a rw active mounted fs.
So if the buffer is dirty we should panic.

That's not a special case for the raid code either, because if the buffer
is dirty then the raid code destroys the buffer putting it in the
freelist.

!buffer_dirty(bh) &&


If the buffer is locked it means there's an SMP race in wait_on_buffer()
and we should panic.

!buffer_locked(bh) &&

This waitqueue check is meaningless. If somebody is sleeping on a the
waitqueue and the buffer is not locked it's a sign of a SMP race condition
and we should panic.

!waitqueue_active(&bh->b_wait)) {

So this is an patch incremental with 2.2.14pre11+raid0145-19990824-2.2.11
that should allow you to use the raid code safely with 2.2.14pre11:

--- 2.2.14pre11-raid/fs/buffer.c.~1~ Mon Dec 6 18:40:45 1999
+++ 2.2.14pre11-raid/fs/buffer.c Mon Dec 6 18:45:33 1999
@@ -641,7 +641,6 @@
}
}

-#if 0
void set_blocksize(kdev_t dev, int size)
{
extern int *blksize_size[];
@@ -699,79 +698,6 @@
}
}
}
-
-#else
-void set_blocksize(kdev_t dev, int size)
-{
- extern int *blksize_size[];
- int i, nlist;
- struct buffer_head * bh, *bhnext;
-
- if (!blksize_size[MAJOR(dev)])
- return;
-
- /* Size must be a power of two, and between 512 and PAGE_SIZE */
- if (size > PAGE_SIZE || size < 512 || (size & (size-1)))
- panic("Invalid blocksize passed to set_blocksize");
-
- if (blksize_size[MAJOR(dev)][MINOR(dev)] == 0 && size == BLOCK_SIZE) {
- blksize_size[MAJOR(dev)][MINOR(dev)] = size;
- return;
- }
- if (blksize_size[MAJOR(dev)][MINOR(dev)] == size)
- return;
- sync_buffers(dev, 2);
- blksize_size[MAJOR(dev)][MINOR(dev)] = size;
-
- /* We need to be quite careful how we do this - we are moving entries
- * around on the free list, and we can get in a loop if we are not careful.
- */
- for(nlist = 0; nlist < NR_LIST; nlist++) {
- bh = lru_list[nlist];
- for (i = nr_buffers_type[nlist]*2 ; --i > 0 ; bh = bhnext) {
- if(!bh)
- break;
-
- bhnext = bh->b_next_free;
- if (bh->b_dev != dev)
- continue;
- if (bh->b_size == size)
- continue;
- if (bhnext)
- bhnext->b_count++;
- wait_on_buffer(bh);
- if (bh->b_dev == dev && bh->b_size != size) {
- clear_bit(BH_Dirty, &bh->b_state);
- clear_bit(BH_Uptodate, &bh->b_state);
- clear_bit(BH_Req, &bh->b_state);
- bh->b_flushtime = 0;
- }
-
- /*
- * lets be mega-conservative about what to free:
- */
- if (!(bh->b_dev != dev) &&
- !(bh->b_size == size) &&
- !bh->b_count &&
- !buffer_protected(bh) &&
- !buffer_dirty(bh) &&
- !buffer_locked(bh) &&
- !waitqueue_active(&bh->b_wait)) {
- remove_from_hash_queue(bh);
- bh->b_dev = NODEV;
- refile_buffer(bh);
- try_to_free_buffers(buffer_page(bh));
- } else {
- remove_from_queues(bh);
- bh->b_dev=B_FREE;
- insert_into_queues(bh);
- }
- if (bhnext)
- bhnext->b_count--;
- }
- }
-}
-#endif

/*
* This function knows that we do a linear pass over the whole array,
Andrea


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/

\
 
 \ /
  Last update: 2005-03-22 13:55    [W:0.651 / U:0.728 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site