patch-pre2.0.12 linux/fs/buffer.c
Next file: linux/include/asm-alpha/alcor.h
Previous file: linux/drivers/sound/Config.in
Back to the patch index
Back to the overall index
- Lines: 223
- Date:
Tue Jun 4 16:41:44 1996
- Orig file:
pre2.0.11/linux/fs/buffer.c
- Orig date:
Mon Jun 3 16:46:56 1996
diff -u --recursive --new-file pre2.0.11/linux/fs/buffer.c linux/fs/buffer.c
@@ -348,7 +348,7 @@
static inline void remove_from_free_list(struct buffer_head * bh)
{
- int isize = BUFSIZE_INDEX(bh->b_size);
+ int isize = BUFSIZE_INDEX(bh->b_size);
if (!(bh->b_prev_free) || !(bh->b_next_free))
panic("VFS: Free block list corrupted");
if(bh->b_dev != B_FREE)
@@ -369,7 +369,7 @@
static inline void remove_from_queues(struct buffer_head * bh)
{
- if(bh->b_dev == B_FREE) {
+ if(bh->b_dev == B_FREE) {
remove_from_free_list(bh); /* Free list entries should not be
in the hash queue */
return;
@@ -410,7 +410,7 @@
static inline void put_last_free(struct buffer_head * bh)
{
- int isize;
+ int isize;
if (!bh)
return;
@@ -432,7 +432,7 @@
static inline void insert_into_queues(struct buffer_head * bh)
{
/* put at end of free list */
- if(bh->b_dev == B_FREE) {
+ if(bh->b_dev == B_FREE) {
put_last_free(bh);
return;
}
@@ -556,7 +556,7 @@
struct buffer_head * bh, * tmp;
struct buffer_head * candidate[NR_LIST];
unsigned int best_time, winner;
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
int buffers[NR_LIST];
int i;
int needed;
@@ -748,7 +748,7 @@
struct buffer_head * getblk(kdev_t dev, int block, int size)
{
struct buffer_head * bh;
- int isize = BUFSIZE_INDEX(size);
+ int isize = BUFSIZE_INDEX(size);
/* Update this for the buffer size lav. */
buffer_usage[isize]++;
@@ -789,7 +789,7 @@
void set_writetime(struct buffer_head * buf, int flag)
{
- int newtime;
+ int newtime;
if (buffer_dirty(buf)) {
/* Move buffer to dirty list if jiffies is clear */
@@ -1106,20 +1106,19 @@
swap_after_unlock_page(page->swap_unlock_entry);
}
-/* Free all temporary buffers belonging to a page. */
+/*
+ * Free all temporary buffers belonging to a page.
+ * This needs to be called with interrupts disabled.
+ */
static inline void free_async_buffers (struct buffer_head * bh)
{
struct buffer_head * tmp;
- unsigned long flags;
tmp = bh;
- save_flags(flags);
- cli();
do {
if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
printk ("Whoops: unlock_buffer: "
"async IO mismatch on page.\n");
- restore_flags(flags);
return;
}
tmp->b_next_free = reuse_list;
@@ -1127,7 +1126,6 @@
clear_bit(BH_FreeOnIO, &tmp->b_state);
tmp = tmp->b_this_page;
} while (tmp != bh);
- restore_flags(flags);
}
/*
@@ -1171,11 +1169,12 @@
next->b_flushtime = 0;
set_bit(BH_Uptodate, &next->b_state);
- /* When we use bmap, we define block zero to represent
- a hole. ll_rw_page, however, may legitimately
- access block zero, and we need to distinguish the
- two cases.
- */
+ /*
+ * When we use bmap, we define block zero to represent
+ * a hole. ll_rw_page, however, may legitimately
+ * access block zero, and we need to distinguish the
+ * two cases.
+ */
if (bmap && !block) {
memset(next->b_data, 0, size);
next->b_count--;
@@ -1211,10 +1210,14 @@
/* The rest of the work is done in mark_buffer_uptodate()
* and unlock_buffer(). */
} else {
+ unsigned long flags;
clear_bit(PG_locked, &page->flags);
set_bit(PG_uptodate, &page->flags);
wake_up(&page->wait);
+ save_flags(flags);
+ cli();
free_async_buffers(bh);
+ restore_flags(flags);
after_unlock_page(page);
}
++current->maj_flt;
@@ -1247,44 +1250,67 @@
*/
void unlock_buffer(struct buffer_head * bh)
{
+ unsigned long flags;
struct buffer_head *tmp;
struct page *page;
- if (!test_bit(BH_FreeOnIO, &bh->b_state)) {
- /* This is a normal buffer. */
- clear_bit(BH_Lock, &bh->b_state);
- wake_up(&bh->b_wait);
+ clear_bit(BH_Lock, &bh->b_state);
+ wake_up(&bh->b_wait);
+
+ if (!test_bit(BH_FreeOnIO, &bh->b_state))
return;
- }
/* This is a temporary buffer used for page I/O. */
page = mem_map + MAP_NR(bh->b_data);
- if (!PageLocked(page)) {
- printk ("Whoops: unlock_buffer: "
- "async io complete on unlocked page\n");
- return;
- }
- if (bh->b_count != 1) {
- printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
- return;
- }
- /* Async buffer_heads are here only as labels for IO, and get
- thrown away once the IO for this page is complete. IO is
- deemed complete once all buffers have been unlocked. */
+ if (!PageLocked(page))
+ goto not_locked;
+ if (bh->b_count != 1)
+ goto bad_count;
+
if (!test_bit(BH_Uptodate, &bh->b_state))
set_bit(PG_error, &page->flags);
- clear_bit(BH_Lock, &bh->b_state);
- wake_up(&bh->b_wait);
- for (tmp = bh; tmp=tmp->b_this_page, tmp!=bh; ) {
- if (test_bit(BH_Lock, &tmp->b_state))
- return;
- }
+
+ /*
+ * Be _very_ careful from here on. Bad things can happen if
+ * two buffer heads end IO at almost the same time and both
+ * decide that the page is now completely done.
+ *
+ * Async buffer_heads are here only as labels for IO, and get
+ * thrown away once the IO for this page is complete. IO is
+ * deemed complete once all buffers have been visited
+ * (b_count==0) and are now unlocked. We must make sure that
+ * only the _last_ buffer that decrements its count is the one
+ * that free's the page..
+ */
+ save_flags(flags);
+ cli();
+ bh->b_count--;
+ tmp = bh;
+ do {
+ if (tmp->b_count)
+ goto still_busy;
+ tmp = tmp->b_this_page;
+ } while (tmp != bh);
+
/* OK, the async IO on this page is complete. */
- if (!clear_bit(PG_locked, &page->flags))
- return;
- wake_up(&page->wait);
free_async_buffers(bh);
+ restore_flags(flags);
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
after_unlock_page(page);
wake_up(&buffer_wait);
+ return;
+
+still_busy:
+ restore_flags(flags);
+ return;
+
+not_locked:
+ printk ("Whoops: unlock_buffer: async io complete on unlocked page\n");
+ return;
+
+bad_count:
+ printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
+ return;
}
/*
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this