From 1138bf4c1cfa77a2458c638ce7e1f052f99d7b44 Mon Sep 17 00:00:00 2001
From: Anton Altaparmakov <aia21@cantab.net>
Date: Sat, 13 Jul 2002 03:30:06 +0100
Subject: [PATCH] NTFS: 2.0.19 - Fix race condition, improvements, and
 optimizations in i/o interface. - Apply block optimization added to
 fs/ntfs/aops.c::ntfs_read_block()   to
 fs/ntfs/compress.c::ntfs_file_read_compressed_block() as well. - Drop the
 "file" from ntfs_file_read_compressed_block(). - Rename
 fs/ntfs/aops.c::ntfs_enb_buffer_read_async() to  
 ntfs_end_buffer_async_read() (more like the fs/buffer.c counterpart). -
 Update ntfs_end_buffer_async_read() with the improved logic from   its
 updated counterpart fs/buffer.c::end_buffer_async_read(). Apply   further
 logic improvements to better determine when we set PageError. - Update
 submission of buffers in fs/ntfs/aops.c::ntfs_read_block() to   check for the
 buffers being uptodate first in line with the updated  
 fs/buffer.c::block_read_full_page(). This plugs a small race   condition.

---
 Documentation/filesystems/ntfs.txt |  3 ++
 fs/ntfs/ChangeLog                  | 20 ++++++---
 fs/ntfs/Makefile                   |  2 +-
 fs/ntfs/aops.c                     | 71 ++++++++++++++++++------------
 fs/ntfs/compress.c                 | 40 ++++++++++++-----
 fs/ntfs/inode.c                    | 22 ++++-----
 fs/ntfs/ntfs.h                     |  2 +-
 7 files changed, 101 insertions(+), 59 deletions(-)

diff --git a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
index 8ca1aa3bfdd9..f7cfd264d9db 100644
--- a/Documentation/filesystems/ntfs.txt
+++ b/Documentation/filesystems/ntfs.txt
@@ -247,6 +247,9 @@ ChangeLog
 
 Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
 
+2.0.19:
+	- Fix race condition and improvements in block i/o interface.
+	- Optimization when reading compressed files.
 2.0.18:
 	- Fix race condition in reading of compressed files.
 2.0.17:
diff --git a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
index c397a20d3014..5f24f047d2c8 100644
--- a/fs/ntfs/ChangeLog
+++ b/fs/ntfs/ChangeLog
@@ -9,12 +9,22 @@ ToDo:
 	  read() will fail when s_maxbytes is reached? -> Investigate this.
 	- Implement/allow non-resident index bitmaps in dir.c::ntfs_readdir()
 	  and then also consider initialized_size w.r.t. the bitmaps, etc.
-	- Consider if ntfs_file_read_compressed_block() shouldn't be coping
-	  with initialized_size < data_size. I don't think it can happen but
-	  it requires more careful consideration.
 	- Enable NFS exporting of NTFS.
-	- Apply block resolution optimization from aops.c::ntfs_read_block() to
-	  compress.c::ntfs_file_read_compressed_block() as well.
+
+2.0.19 - Fix race condition, improvements, and optimizations in i/o interface.
+
+	- Apply block optimization added to fs/ntfs/aops.c::ntfs_read_block()
+	  to fs/ntfs/compress.c::ntfs_file_read_compressed_block() as well.
+	- Drop the "file" from ntfs_file_read_compressed_block().
+	- Rename fs/ntfs/aops.c::ntfs_enb_buffer_read_async() to
+	  ntfs_end_buffer_async_read() (more like the fs/buffer.c counterpart).
+	- Update ntfs_end_buffer_async_read() with the improved logic from
+	  its updated counterpart fs/buffer.c::end_buffer_async_read(). Apply
+	  further logic improvements to better determine when we set PageError.
+	- Update submission of buffers in fs/ntfs/aops.c::ntfs_read_block() to
+	  check for the buffers being uptodate first in line with the updated
+	  fs/buffer.c::block_read_full_page(). This plugs a small race
+	  condition.
 
 2.0.18 - Fix race condition in reading of compressed files.
 
diff --git a/fs/ntfs/Makefile b/fs/ntfs/Makefile
index e1ae35778072..4ddae54cb981 100644
--- a/fs/ntfs/Makefile
+++ b/fs/ntfs/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_NTFS_FS) += ntfs.o
 ntfs-objs := aops.o attrib.o compress.o debug.o dir.o file.o inode.o mft.o \
 	     mst.o namei.o super.o sysctl.o time.o unistr.o upcase.o
 
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.18\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.19\"
 
 ifeq ($(CONFIG_NTFS_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c
index da27758856f8..195ea920db71 100644
--- a/fs/ntfs/aops.c
+++ b/fs/ntfs/aops.c
@@ -30,7 +30,7 @@
 #include "ntfs.h"
 
 /**
- * ntfs_end_buffer_read_async - async io completion for reading attributes
+ * ntfs_end_buffer_async_read - async io completion for reading attributes
  * @bh:		buffer head on which io is completed
  * @uptodate:	whether @bh is now uptodate or not
  *
@@ -45,26 +45,23 @@
  * record size, and index_block_size_bits, to the log(base 2) of the ntfs
  * record size.
  */
-static void ntfs_end_buffer_read_async(struct buffer_head *bh, int uptodate)
+static void ntfs_end_buffer_async_read(struct buffer_head *bh, int uptodate)
 {
 	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
 	unsigned long flags;
 	struct buffer_head *tmp;
 	struct page *page;
 	ntfs_inode *ni;
-
-	if (likely(uptodate))
-		set_buffer_uptodate(bh);
-	else
-		clear_buffer_uptodate(bh);
+	int page_uptodate = 1;
 
 	page = bh->b_page;
-
 	ni = NTFS_I(page->mapping->host);
 
 	if (likely(uptodate)) {
 		s64 file_ofs;
 
+		set_buffer_uptodate(bh);
+
 		file_ofs = (page->index << PAGE_CACHE_SHIFT) + bh_offset(bh);
 		/* Check for the current buffer head overflowing. */
 		if (file_ofs + bh->b_size > ni->initialized_size) {
@@ -78,22 +75,28 @@ static void ntfs_end_buffer_read_async(struct buffer_head *bh, int uptodate)
 			flush_dcache_page(page);
 			kunmap_atomic(addr, KM_BIO_SRC_IRQ);
 		}
-	} else
+	} else {
+		clear_buffer_uptodate(bh);
+		ntfs_error(ni->vol->sb, "Buffer I/O error, logical block %Lu.",
+				(unsigned long long)bh->b_blocknr);
 		SetPageError(page);
+	}
 
 	spin_lock_irqsave(&page_uptodate_lock, flags);
 	clear_buffer_async_read(bh);
 	unlock_buffer(bh);
-
-	tmp = bh->b_this_page;
-	while (tmp != bh) {
-		if (buffer_locked(tmp)) {
-			if (buffer_async_read(tmp))
+	tmp = bh;
+	do {
+		if (!buffer_uptodate(tmp))
+			page_uptodate = 0;
+		if (buffer_async_read(tmp)) {
+			if (likely(buffer_locked(tmp)))
 				goto still_busy;
-		} else if (!buffer_uptodate(tmp))
-			SetPageError(page);
+			/* Async buffers must be locked. */
+			BUG();
+		}
 		tmp = tmp->b_this_page;
-	}
+	} while (tmp != bh);
 	spin_unlock_irqrestore(&page_uptodate_lock, flags);
 	/*
 	 * If none of the buffers had errors then we can set the page uptodate,
@@ -101,7 +104,7 @@ static void ntfs_end_buffer_read_async(struct buffer_head *bh, int uptodate)
 	 * attribute is mst protected, i.e. if NInoMstProteced(ni) is true.
 	 */
 	if (!NInoMstProtected(ni)) {
-		if (likely(!PageError(page)))
+		if (likely(page_uptodate && !PageError(page)))
 			SetPageUptodate(page);
 		unlock_page(page);
 		return;
@@ -127,12 +130,15 @@ static void ntfs_end_buffer_read_async(struct buffer_head *bh, int uptodate)
 		}
 		flush_dcache_page(page);
 		kunmap_atomic(addr, KM_BIO_SRC_IRQ);
-		if (likely(!nr_err && recs))
-			SetPageUptodate(page);
-		else {
-			ntfs_error(ni->vol->sb, "Setting page error, index "
-					"0x%lx.", page->index);
-			SetPageError(page);
+		if (likely(!PageError(page))) {
+			if (likely(!nr_err && recs)) {
+				if (likely(page_uptodate))
+					SetPageUptodate(page);
+			} else {
+				ntfs_error(ni->vol->sb, "Setting page error, "
+						"index 0x%lx.", page->index);
+				SetPageError(page);
+			}
 		}
 	}
 	unlock_page(page);
@@ -282,16 +288,23 @@ static int ntfs_read_block(struct page *page)
 
 	/* Check we have at least one buffer ready for i/o. */
 	if (nr) {
+		struct buffer_head *tbh;
+
 		/* Lock the buffers. */
 		for (i = 0; i < nr; i++) {
-			struct buffer_head *tbh = arr[i];
+			tbh = arr[i];
 			lock_buffer(tbh);
-			tbh->b_end_io = ntfs_end_buffer_read_async;
+			tbh->b_end_io = ntfs_end_buffer_async_read;
 			set_buffer_async_read(tbh);
 		}
 		/* Finally, start i/o on the buffers. */
-		for (i = 0; i < nr; i++)
-			submit_bh(READ, arr[i]);
+		for (i = 0; i < nr; i++) {
+			tbh = arr[i];
+			if (likely(!buffer_uptodate(tbh)))
+				submit_bh(READ, tbh);
+			else
+				ntfs_end_buffer_async_read(tbh, 1);
+		}
 		return 0;
 	}
 	/* No i/o was scheduled on any of the buffers. */
@@ -349,7 +362,7 @@ int ntfs_readpage(struct file *file, struct page *page)
 			}
 			/* Compressed data streams are handled in compress.c. */
 			if (NInoCompressed(ni))
-				return ntfs_file_read_compressed_block(page);
+				return ntfs_read_compressed_block(page);
 		}
 		/* Normal data stream. */
 		return ntfs_read_block(page);
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
index a55a27900dcd..b0a3b4da132c 100644
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -386,7 +386,7 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
 }
 
 /**
- * ntfs_file_read_compressed_block - read a compressed block into the page cache
+ * ntfs_read_compressed_block - read a compressed block into the page cache
  * @page:	locked page in the compression block(s) we need to read
  *
  * When we are called the page has already been verified to be locked and the
@@ -418,14 +418,15 @@ static int ntfs_decompress(struct page *dest_pages[], int *dest_index,
  * initialized_size is less than data_size. This should be safe because of the
  * nature of the compression algorithm used. Just in case we check and output
  * an error message in read inode if the two sizes are not equal for a
- * compressed file.
+ * compressed file. (AIA)
  */
-int ntfs_file_read_compressed_block(struct page *page)
+int ntfs_read_compressed_block(struct page *page)
 {
 	struct address_space *mapping = page->mapping;
 	ntfs_inode *ni = NTFS_I(mapping->host);
 	ntfs_volume *vol = ni->vol;
 	struct super_block *sb = vol->sb;
+	run_list_element *rl;
 	unsigned long block_size = sb->s_blocksize;
 	unsigned char block_size_bits = sb->s_blocksize_bits;
 	u8 *cb, *cb_pos, *cb_end;
@@ -532,14 +533,23 @@ int ntfs_file_read_compressed_block(struct page *page)
 	nr_bhs = 0;
 
 	/* Read all cb buffer heads one cluster at a time. */
+	rl = NULL;
 	for (vcn = start_vcn, start_vcn += cb_clusters; vcn < start_vcn;
 			vcn++) {
 		BOOL is_retry = FALSE;
-retry_remap:
-		/* Find lcn of vcn and convert it into blocks. */
-		down_read(&ni->run_list.lock);
-		lcn = vcn_to_lcn(ni->run_list.rl, vcn);
-		up_read(&ni->run_list.lock);
+
+		if (!rl) {
+lock_retry_remap:
+			down_read(&ni->run_list.lock);
+			rl = ni->run_list.rl;
+		}
+		if (likely(rl != NULL)) {
+			/* Seek to element containing target vcn. */
+			while (rl->length && rl[1].vcn <= vcn)
+				rl++;
+			lcn = vcn_to_lcn(rl, vcn);
+		} else
+			lcn = (LCN)LCN_RL_NOT_MAPPED;
 		ntfs_debug("Reading vcn = 0x%Lx, lcn = 0x%Lx.",
 				(long long)vcn, (long long)lcn);
 		if (lcn < 0) {
@@ -552,9 +562,13 @@ int ntfs_file_read_compressed_block(struct page *page)
 			if (is_retry || lcn != LCN_RL_NOT_MAPPED)
 				goto rl_err;
 			is_retry = TRUE;
-			/* Map run list of current extent and retry. */
+			/*
+			 * Attempt to map run list, dropping lock for the
+			 * duration.
+			 */
+			up_read(&ni->run_list.lock);
 			if (!map_run_list(ni, vcn))
-				goto retry_remap;
+				goto lock_retry_remap;
 			goto map_rl_err;
 		}
 		block = lcn << vol->cluster_size_bits >> block_size_bits;
@@ -568,6 +582,10 @@ int ntfs_file_read_compressed_block(struct page *page)
 		} while (++block < max_block);
 	}
 
+	/* Release the lock if we took it. */
+	if (rl)
+		up_read(&ni->run_list.lock);
+
 	/* Setup and initiate io on all buffer heads. */
 	for (i = 0; i < nr_bhs; i++) {
 		struct buffer_head *tbh = bhs[i];
@@ -828,11 +846,13 @@ int ntfs_file_read_compressed_block(struct page *page)
 	goto err_out;
 
 rl_err:
+	up_read(&ni->run_list.lock);
 	ntfs_error(vol->sb, "vcn_to_lcn() failed. Cannot read compression "
 			"block.");
 	goto err_out;
 
 getblk_err:
+	up_read(&ni->run_list.lock);
 	ntfs_error(vol->sb, "getblk() failed. Cannot read compression block.");
 
 err_out:
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 4cb37ec9ea0d..53e85f86e2f5 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1007,19 +1007,15 @@ static int ntfs_read_locked_inode(struct inode *vi)
 				ni->_ICF(compressed_size) = sle64_to_cpu(
 					ctx->attr->_ANR(compressed_size));
 				if (vi->i_size != ni->initialized_size)
-					ntfs_warning(vi->i_sb, "Compressed "
-							"file with data_size "
-							"unequal to "
-							"initialized size "
-							"found. This will "
-							"probably cause "
-							"problems when trying "
-							"to access the file. "
-							"Please notify "
-							"linux-ntfs-dev@"
-							"lists.sf.net that you"
-							"saw this message."
-							"Thanks!");
+					ntfs_warning(vi->i_sb, "BUG: Found "
+						"compressed file with "
+						"data_size not equal to "
+						"initialized_size. This will "
+						"probably cause problems when "
+						"trying to access the file. "
+						"Please notify linux-ntfs-dev@"
+						"lists.sf.net that you saw "
+						"this message. Thanks!");
 			}
 		} else { /* Resident attribute. */
 			/*
diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
index e40106d6b92f..8cbecfa2200d 100644
--- a/fs/ntfs/ntfs.h
+++ b/fs/ntfs/ntfs.h
@@ -158,7 +158,7 @@ static inline struct page *ntfs_map_page(struct address_space *mapping,
 /* Declarations of functions and global variables. */
 
 /* From fs/ntfs/compress.c */
-extern int ntfs_file_read_compressed_block(struct page *page);
+extern int ntfs_read_compressed_block(struct page *page);
 
 /* From fs/ntfs/super.c */
 #define default_upcase_len 0x10000
-- 
2.30.9