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

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