Messages in this thread Patch in this message | | | Date | Mon, 6 Dec 1999 19:09:44 +0100 (CET) | From | Andrea Arcangeli <> | Subject | Re: 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/
| |