--- 2.3.16/fs/buffer.c Wed Sep 1 09:30:03 1999 +++ buffer.c Thu Sep 2 10:26:09 1999 @@ -511,29 +511,24 @@ bh->b_next_free = bh->b_prev_free = NULL; } -/* The following two functions must operate atomically - * because they control the visibility of a buffer head +/* The following operation control the visibility of a buffer head * to the rest of the kernel. + * A buffer must be always linked into the lru list before being visible + * in the hashtable. This because once the buffer is visible in the hashtable + * somebody may refile it and add it to the lru list before us. So if we + * would link the buffer into the lru list, after making it visible in the + * hash, we could risk to insert the buffer two times in the lru list. */ -static __inline__ void __remove_from_queues(struct buffer_head *bh) -{ - write_lock(&hash_table_lock); - if (bh->b_pprev) - __hash_unlink(bh); - __remove_from_lru_list(bh, bh->b_list); - write_unlock(&hash_table_lock); -} - static void insert_into_queues(struct buffer_head *bh) { struct buffer_head **head = &hash(bh->b_dev, bh->b_blocknr); spin_lock(&lru_list_lock); + __insert_into_lru_list(bh, bh->b_list); + spin_unlock(&lru_list_lock); write_lock(&hash_table_lock); __hash_link(bh, head); - __insert_into_lru_list(bh, bh->b_list); write_unlock(&hash_table_lock); - spin_unlock(&lru_list_lock); } /* This function must only run if there are no other @@ -604,7 +599,7 @@ void set_blocksize(kdev_t dev, int size) { extern int *blksize_size[]; - int i, nlist; + int i, nlist, freeing; struct buffer_head * bh, *bhnext; if (!blksize_size[MAJOR(dev)]) @@ -651,8 +646,19 @@ clear_bit(BH_Uptodate, &bh->b_state); clear_bit(BH_Req, &bh->b_state); } - if (atomic_read(&bh->b_count) == 0) { - __remove_from_queues(bh); + + freeing = 0; + write_lock(&hash_table_lock); + if (atomic_read(&bh->b_count) == 0) + { + freeing = 1; + if (bh->b_pprev) + __hash_unlink(bh); + } + write_unlock(&hash_table_lock); + if (freeing) + { + __remove_from_lru_list(bh, bh->b_list); put_last_free(bh); } }