Skip to contentSkip to conversation

Buffered write deadlock fix and new aops for 2.6.22-rc2-mm1

PATCH Applied [email protected] 5 participants 55 messages May 25, 2007 – Jun 1, 2007

Subsystems (23)
lore
Share & tools
lore
#0 lore raw

cover Buffered write deadlock fix and new aops for 2.6.22-rc2-mm1

Hi,

This is a resync of the new aops patches to 2.6.22-rc2-mm1

Only one more conversion broken this time, so we're doing OK. AFFS compile is broken due to cont_prepare_write disappearing, and me not bringing the conversion patch uptodate (which I won't do again until something happens with this patchset -- its only affs!). Reiser4 broken because it lost filemap_copy_from_user (it's deadlocky as well, yay!).

Still unfortunately missing the OCFS2 and GFS2 conversions, which allowed us to remove a lot of code -- I won't ask the maintainers to redo them either until the patchset gets somewhere.

Highlight of this release is the reiserfs conversion, and the removal of the reiserfs-specific generic_cont_expand helper. Also fixed a bug in my pagecache directory conversions.

Please merge?

#1 lore raw

PATCH 1/41 mm: revert KERNEL_DS buffered write optimisation

Revert the patch from Neil Brown to optimise NFSD writev handling.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
Cc Neil Brown <[email protected]>
 mm/filemap.c |   32 +++++++++++++-------------------
 1 file changed, 13 insertions(+), 19 deletions(-)
mm/filemap.c +13 -19
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1948,27 +1948,21 @@ generic_file_buffered_write(struct kiocb
 		/* Limit the size of the copy to the caller's write size */
 		bytes = min(bytes, count);
 
-		/* We only need to worry about prefaulting when writes are from
-		 * user-space.  NFSd uses vfs_writev with several non-aligned
-		 * segments in the vector, and limiting to one segment a time is
-		 * a noticeable performance for re-write
+		/*
+		 * Limit the size of the copy to that of the current segment,
+		 * because fault_in_pages_readable() doesn't know how to walk
+		 * segments.
 		 */
-		if (!segment_eq(get_fs(), KERNEL_DS)) {
-			/*
-			 * Limit the size of the copy to that of the current
-			 * segment, because fault_in_pages_readable() doesn't
-			 * know how to walk segments.
-			 */
-			bytes = min(bytes, cur_iov->iov_len - iov_base);
+		bytes = min(bytes, cur_iov->iov_len - iov_base);
+
+		/*
+		 * Bring in the user page that we will copy from _first_.
+		 * Otherwise there's a nasty deadlock on copying from the
+		 * same page as we're writing to, without it being marked
+		 * up-to-date.
+		 */
+		fault_in_pages_readable(buf, bytes);
 
-			/*
-			 * Bring in the user page that we will copy from
-			 * _first_.  Otherwise there's a nasty deadlock on
-			 * copying from the same page as we're writing to,
-			 * without it being marked up-to-date.
-			 */
-			fault_in_pages_readable(buf, bytes);
-		}
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
 		if (!page) {
 			status = -ENOMEM;
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#2 lore raw

PATCH 2/41 Revert 81b0c8713385ce1b1b9058e916edcf9561ad76d6

In reply to [email protected]

This was a bugfix against 6527c2bdf1f833cc18e8f42bd97973d583e4aa83, which we also revert.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c |    9 +--------
 mm/filemap.h |    4 ++--
 2 files changed, 3 insertions(+), 10 deletions(-)
mm/filemap.c +1 -8
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1969,12 +1969,6 @@ generic_file_buffered_write(struct kiocb
 			break;
 		}
 
-		if (unlikely(bytes == 0)) {
-			status = 0;
-			copied = 0;
-			goto zero_length_segment;
-		}
-
 		status = a_ops->prepare_write(file, page, offset, offset+bytes);
 		if (unlikely(status)) {
 			loff_t isize = i_size_read(inode);
@@ -2004,8 +1998,7 @@ generic_file_buffered_write(struct kiocb
 			page_cache_release(page);
 			continue;
 		}
-zero_length_segment:
-		if (likely(copied >= 0)) {
+		if (likely(copied > 0)) {
 			if (!status)
 				status = copied;
 
mm/filemap.h +2 -2
Index: linux-2.6/mm/filemap.h
===================================================================
@@ -87,7 +87,7 @@ filemap_set_next_iovec(const struct iove
 	const struct iovec *iov = *iovp;
 	size_t base = *basep;
 
-	do {
+	while (bytes) {
 		int copy = min(bytes, iov->iov_len - base);
 
 		bytes -= copy;
@@ -96,7 +96,7 @@ filemap_set_next_iovec(const struct iove
 			iov++;
 			base = 0;
 		}
-	} while (bytes);
+	}
 	*iovp = iov;
 	*basep = base;
 }
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#3 lore raw

PATCH 3/41 Revert 6527c2bdf1f833cc18e8f42bd97973d583e4aa83

In reply to [email protected]

This patch fixed the following bug:

When prefaulting in the pages in generic_file_buffered_write(), we only faulted in the pages for the firts segment of the iovec. If the second of successive segment described a mmapping of the page into which we're write()ing, and that page is not up-to-date, the fault handler tries to lock the already-locked page (to bring it up to date) and deadlocks.

An exploit for this bug is in writev-deadlock-demo.c, in
http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz.

(These demos assume blocksize < PAGE_CACHE_SIZE).

The problem with this fix is that it takes the kernel back to doing a single prepare_write()/commit_write() per iovec segment. So in the worst case we'll run prepare_write+commit_write 1024 times where we previously would have run it once. The other problem with the fix is that it fix all the locking problems.

<insert numbers obtained via ext3-tools's writev-speed.c here>

And apparently this change killed NFS overwrite performance, because, I suppose, it talks to the server for each prepare_write+commit_write.

So just back that patch out - we'll be fixing the deadlock by other means.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>

Nick says: also it only ever actually papered over the bug, because after faulting in the pages, they might be unmapped or reclaimed.

 mm/filemap.c |   18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)
mm/filemap.c +7 -11
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1939,21 +1939,14 @@ generic_file_buffered_write(struct kiocb
 	do {
 		unsigned long index;
 		unsigned long offset;
+		unsigned long maxlen;
 		size_t copied;
 
 		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
 		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
-
-		/* Limit the size of the copy to the caller's write size */
-		bytes = min(bytes, count);
-
-		/*
-		 * Limit the size of the copy to that of the current segment,
-		 * because fault_in_pages_readable() doesn't know how to walk
-		 * segments.
-		 */
-		bytes = min(bytes, cur_iov->iov_len - iov_base);
+		if (bytes > count)
+			bytes = count;
 
 		/*
 		 * Bring in the user page that we will copy from _first_.
@@ -1961,7 +1954,10 @@ generic_file_buffered_write(struct kiocb
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
 		 */
-		fault_in_pages_readable(buf, bytes);
+		maxlen = cur_iov->iov_len - iov_base;
+		if (maxlen > bytes)
+			maxlen = bytes;
+		fault_in_pages_readable(buf, maxlen);
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
 		if (!page) {
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#4 lore raw

PATCH 4/41 mm: clean up buffered write code

In reply to [email protected]

Rename some variables and fix some types.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c |   35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)
mm/filemap.c +18 -17
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1912,16 +1912,15 @@ generic_file_buffered_write(struct kiocb
 		size_t count, ssize_t written)
 {
 	struct file *file = iocb->ki_filp;
-	struct address_space * mapping = file->f_mapping;
+	struct address_space *mapping = file->f_mapping;
 	const struct address_space_operations *a_ops = mapping->a_ops;
 	struct inode 	*inode = mapping->host;
 	long		status = 0;
 	struct page	*page;
 	struct page	*cached_page = NULL;
-	size_t		bytes;
 	struct pagevec	lru_pvec;
 	const struct iovec *cur_iov = iov; /* current iovec */
-	size_t		iov_base = 0;	   /* offset in the current iovec */
+	size_t		iov_offset = 0;	   /* offset in the current iovec */
 	char __user	*buf;
 
 	pagevec_init(&lru_pvec, 0);
@@ -1932,31 +1931,33 @@ generic_file_buffered_write(struct kiocb
 	if (likely(nr_segs == 1))
 		buf = iov->iov_base + written;
 	else {
-		filemap_set_next_iovec(&cur_iov, &iov_base, written);
-		buf = cur_iov->iov_base + iov_base;
+		filemap_set_next_iovec(&cur_iov, &iov_offset, written);
+		buf = cur_iov->iov_base + iov_offset;
 	}
 
 	do {
-		unsigned long index;
-		unsigned long offset;
-		unsigned long maxlen;
-		size_t copied;
+		pgoff_t index;		/* Pagecache index for current page */
+		unsigned long offset;	/* Offset into pagecache page */
+		unsigned long maxlen;	/* Bytes remaining in current iovec */
+		size_t bytes;		/* Bytes to write to page */
+		size_t copied;		/* Bytes copied from user */
 
-		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		offset = (pos & (PAGE_CACHE_SIZE - 1));
 		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
 		if (bytes > count)
 			bytes = count;
 
+		maxlen = cur_iov->iov_len - iov_offset;
+		if (maxlen > bytes)
+			maxlen = bytes;
+
 		/*
 		 * Bring in the user page that we will copy from _first_.
 		 * Otherwise there's a nasty deadlock on copying from the
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
 		 */
-		maxlen = cur_iov->iov_len - iov_base;
-		if (maxlen > bytes)
-			maxlen = bytes;
 		fault_in_pages_readable(buf, maxlen);
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
@@ -1987,7 +1988,7 @@ generic_file_buffered_write(struct kiocb
 							buf, bytes);
 		else
 			copied = filemap_copy_from_user_iovec(page, offset,
-						cur_iov, iov_base, bytes);
+						cur_iov, iov_offset, bytes);
 		flush_dcache_page(page);
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
 		if (status == AOP_TRUNCATED_PAGE) {
@@ -2005,12 +2006,12 @@ generic_file_buffered_write(struct kiocb
 				buf += status;
 				if (unlikely(nr_segs > 1)) {
 					filemap_set_next_iovec(&cur_iov,
-							&iov_base, status);
+							&iov_offset, status);
 					if (count)
 						buf = cur_iov->iov_base +
-							iov_base;
+							iov_offset;
 				} else {
-					iov_base += status;
+					iov_offset += status;
 				}
 			}
 		}
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#5 lore raw

PATCH 5/41 mm: debug write deadlocks

In reply to [email protected]

Allow CONFIG_DEBUG_VM to switch off the prefaulting logic, to simulate the difficult race where the page may be unmapped before calling copy_from_user. Makes the race much easier to hit.

This is useful for demonstration and testing purposes, but is removed in a subsequent patch.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c |    2 ++
 1 file changed, 2 insertions(+)
mm/filemap.c +2 -0
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1952,6 +1952,7 @@ generic_file_buffered_write(struct kiocb
 		if (maxlen > bytes)
 			maxlen = bytes;
 
+#ifndef CONFIG_DEBUG_VM
 		/*
 		 * Bring in the user page that we will copy from _first_.
 		 * Otherwise there's a nasty deadlock on copying from the
@@ -1959,6 +1960,7 @@ generic_file_buffered_write(struct kiocb
 		 * up-to-date.
 		 */
 		fault_in_pages_readable(buf, maxlen);
+#endif
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
 		if (!page) {
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#6 lore raw

PATCH 6/41 mm: trim more holes

In reply to [email protected]

If prepare_write fails with AOP_TRUNCATED_PAGE, or if commit_write fails, then we may have failed the write operation despite prepare_write having instantiated blocks past i_size. Fix this, and consolidate the trimming into one place.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c |   80 +++++++++++++++++++++++++++++------------------------------
 1 file changed, 40 insertions(+), 40 deletions(-)
mm/filemap.c +40 -40
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1969,22 +1969,9 @@ generic_file_buffered_write(struct kiocb
 		}
 
 		status = a_ops->prepare_write(file, page, offset, offset+bytes);
-		if (unlikely(status)) {
-			loff_t isize = i_size_read(inode);
+		if (unlikely(status))
+			goto fs_write_aop_error;
 
-			if (status != AOP_TRUNCATED_PAGE)
-				unlock_page(page);
-			page_cache_release(page);
-			if (status == AOP_TRUNCATED_PAGE)
-				continue;
-			/*
-			 * prepare_write() may have instantiated a few blocks
-			 * outside i_size.  Trim these off again.
-			 */
-			if (pos + bytes > isize)
-				vmtruncate(inode, isize);
-			break;
-		}
 		if (likely(nr_segs == 1))
 			copied = filemap_copy_from_user(page, offset,
 							buf, bytes);
@@ -1993,40 +1980,53 @@ generic_file_buffered_write(struct kiocb
 						cur_iov, iov_offset, bytes);
 		flush_dcache_page(page);
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
-		if (status == AOP_TRUNCATED_PAGE) {
-			page_cache_release(page);
-			continue;
+		if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
+			goto fs_write_aop_error;
+		if (unlikely(copied != bytes)) {
+			status = -EFAULT;
+			goto fs_write_aop_error;
 		}
-		if (likely(copied > 0)) {
-			if (!status)
-				status = copied;
+		if (unlikely(status > 0)) /* filesystem did partial write */
+			copied = status;
 
-			if (status >= 0) {
-				written += status;
-				count -= status;
-				pos += status;
-				buf += status;
-				if (unlikely(nr_segs > 1)) {
-					filemap_set_next_iovec(&cur_iov,
-							&iov_offset, status);
-					if (count)
-						buf = cur_iov->iov_base +
-							iov_offset;
-				} else {
-					iov_offset += status;
-				}
+		if (likely(copied > 0)) {
+			written += copied;
+			count -= copied;
+			pos += copied;
+			buf += copied;
+			if (unlikely(nr_segs > 1)) {
+				filemap_set_next_iovec(&cur_iov,
+						&iov_offset, copied);
+				if (count)
+					buf = cur_iov->iov_base + iov_offset;
+			} else {
+				iov_offset += copied;
 			}
 		}
-		if (unlikely(copied != bytes))
-			if (status >= 0)
-				status = -EFAULT;
 		unlock_page(page);
 		mark_page_accessed(page);
 		page_cache_release(page);
-		if (status < 0)
-			break;
 		balance_dirty_pages_ratelimited(mapping);
 		cond_resched();
+		continue;
+
+fs_write_aop_error:
+		if (status != AOP_TRUNCATED_PAGE)
+			unlock_page(page);
+		page_cache_release(page);
+
+		/*
+		 * prepare_write() may have instantiated a few blocks
+		 * outside i_size.  Trim these off again. Don't need
+		 * i_size_read because we hold i_mutex.
+		 */
+		if (pos + bytes > inode->i_size)
+			vmtruncate(inode, inode->i_size);
+		if (status == AOP_TRUNCATED_PAGE)
+			continue;
+		else
+			break;
+
 	} while (count);
 	*ppos = pos;
 
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#7 lore raw

PATCH 7/41 mm: buffered write cleanup

In reply to [email protected]

Quite a bit of code is used in maintaining these "cached pages" that are probably pretty unlikely to get used. It would require a narrow race where the page is inserted concurrently while this process is allocating a page in order to create the spare page. Then a multi-page write into an uncached part of the file, to make use of it.

Next, the buffered write path (and others) uses its own LRU pagevec when it should be just using the per-CPU LRU pagevec (which will cut down on both data and code size cacheline footprint). Also, these private LRU pagevecs are emptied after just a very short time, in contrast with the per-CPU pagevecs that are persistent. Net result: 7.3 times fewer lru_lock acquisitions required to add the pages to pagecache for a bulk write (in 4K chunks).

[this gets rid of some cond_resched() calls in readahead.c and mpage.c due
to clashes in -mm. What put them there, and why? ]

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 fs/mpage.c     |   10 ---
 mm/filemap.c   |  145 ++++++++++++++++++++++-----------------------------------
 mm/readahead.c |   24 +++------
 3 files changed, 66 insertions(+), 113 deletions(-)
mm/filemap.c +56 -89
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -666,27 +666,22 @@ EXPORT_SYMBOL(find_lock_page);
 struct page *find_or_create_page(struct address_space *mapping,
 		unsigned long index, gfp_t gfp_mask)
 {
-	struct page *page, *cached_page = NULL;
+	struct page *page;
 	int err;
 repeat:
 	page = find_lock_page(mapping, index);
 	if (!page) {
-		if (!cached_page) {
-			cached_page =
-				__page_cache_alloc(gfp_mask);
-			if (!cached_page)
-				return NULL;
-		}
-		err = add_to_page_cache_lru(cached_page, mapping,
-					index, gfp_mask);
-		if (!err) {
-			page = cached_page;
-			cached_page = NULL;
-		} else if (err == -EEXIST)
-			goto repeat;
+		page = __page_cache_alloc(gfp_mask);
+		if (!page)
+			return NULL;
+		err = add_to_page_cache_lru(page, mapping, index, gfp_mask);
+		if (unlikely(err)) {
+			page_cache_release(page);
+			page = NULL;
+			if (err == -EEXIST)
+				goto repeat;
+		}
 	}
-	if (cached_page)
-		page_cache_release(cached_page);
 	return page;
 }
 EXPORT_SYMBOL(find_or_create_page);
@@ -883,11 +878,9 @@ void do_generic_mapping_read(struct addr
 	unsigned long prev_index;
 	unsigned int prev_offset;
 	loff_t isize;
-	struct page *cached_page;
 	int error;
 	struct file_ra_state ra = *_ra;
 
-	cached_page = NULL;
 	index = *ppos >> PAGE_CACHE_SHIFT;
 	next_index = index;
 	prev_index = ra.prev_index;
@@ -1059,23 +1052,20 @@ no_cached_page:
 		 * Ok, it wasn't cached, so we need to create a new
 		 * page..
 		 */
-		if (!cached_page) {
-			cached_page = page_cache_alloc_cold(mapping);
-			if (!cached_page) {
-				desc->error = -ENOMEM;
-				goto out;
-			}
+		page = page_cache_alloc_cold(mapping);
+		if (!page) {
+			desc->error = -ENOMEM;
+			goto out;
 		}
-		error = add_to_page_cache_lru(cached_page, mapping,
+		error = add_to_page_cache_lru(page, mapping,
 						index, GFP_KERNEL);
 		if (error) {
+			page_cache_release(page);
 			if (error == -EEXIST)
 				goto find_page;
 			desc->error = error;
 			goto out;
 		}
-		page = cached_page;
-		cached_page = NULL;
 		goto readpage;
 	}
 
@@ -1084,8 +1074,6 @@ out:
 	_ra->prev_index = prev_index;
 
 	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
-	if (cached_page)
-		page_cache_release(cached_page);
 	if (filp)
 		file_accessed(filp);
 }
@@ -1573,35 +1561,28 @@ static struct page *__read_cache_page(st
 				int (*filler)(void *,struct page*),
 				void *data)
 {
-	struct page *page, *cached_page = NULL;
+	struct page *page;
 	int err;
 repeat:
 	page = find_get_page(mapping, index);
 	if (!page) {
-		if (!cached_page) {
-			cached_page = page_cache_alloc_cold(mapping);
-			if (!cached_page)
-				return ERR_PTR(-ENOMEM);
-		}
-		err = add_to_page_cache_lru(cached_page, mapping,
-					index, GFP_KERNEL);
-		if (err == -EEXIST)
-			goto repeat;
-		if (err < 0) {
+		page = page_cache_alloc_cold(mapping);
+		if (!page)
+			return ERR_PTR(-ENOMEM);
+		err = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL);
+		if (unlikely(err)) {
+			page_cache_release(page);
+			if (err == -EEXIST)
+				goto repeat;
 			/* Presumably ENOMEM for radix tree node */
-			page_cache_release(cached_page);
 			return ERR_PTR(err);
 		}
-		page = cached_page;
-		cached_page = NULL;
 		err = filler(data, page);
 		if (err < 0) {
 			page_cache_release(page);
 			page = ERR_PTR(err);
 		}
 	}
-	if (cached_page)
-		page_cache_release(cached_page);
 	return page;
 }
 
@@ -1679,40 +1660,6 @@ struct page *read_cache_page(struct addr
 EXPORT_SYMBOL(read_cache_page);
 
 /*
- * If the page was newly created, increment its refcount and add it to the
- * caller's lru-buffering pagevec.  This function is specifically for
- * generic_file_write().
- */
-static inline struct page *
-__grab_cache_page(struct address_space *mapping, unsigned long index,
-			struct page **cached_page, struct pagevec *lru_pvec)
-{
-	int err;
-	struct page *page;
-repeat:
-	page = find_lock_page(mapping, index);
-	if (!page) {
-		if (!*cached_page) {
-			*cached_page = page_cache_alloc(mapping);
-			if (!*cached_page)
-				return NULL;
-		}
-		err = add_to_page_cache(*cached_page, mapping,
-					index, GFP_KERNEL);
-		if (err == -EEXIST)
-			goto repeat;
-		if (err == 0) {
-			page = *cached_page;
-			page_cache_get(page);
-			if (!pagevec_add(lru_pvec, page))
-				__pagevec_lru_add(lru_pvec);
-			*cached_page = NULL;
-		}
-	}
-	return page;
-}
-
-/*
  * The logic we want is
  *
  *	if suid or (sgid and xgrp)
@@ -1906,6 +1853,33 @@ generic_file_direct_write(struct kiocb *
 }
 EXPORT_SYMBOL(generic_file_direct_write);
 
+/*
+ * Find or create a page at the given pagecache position. Return the locked
+ * page. This function is specifically for buffered writes.
+ */
+static struct page *__grab_cache_page(struct address_space *mapping,
+							pgoff_t index)
+{
+	int status;
+	struct page *page;
+repeat:
+	page = find_lock_page(mapping, index);
+	if (likely(page))
+		return page;
+
+	page = page_cache_alloc(mapping);
+	if (!page)
+		return NULL;
+	status = add_to_page_cache_lru(page, mapping, index, GFP_KERNEL);
+	if (unlikely(status)) {
+		page_cache_release(page);
+		if (status == -EEXIST)
+			goto repeat;
+		return NULL;
+	}
+	return page;
+}
+
 ssize_t
 generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
 		unsigned long nr_segs, loff_t pos, loff_t *ppos,
@@ -1916,15 +1890,10 @@ generic_file_buffered_write(struct kiocb
 	const struct address_space_operations *a_ops = mapping->a_ops;
 	struct inode 	*inode = mapping->host;
 	long		status = 0;
-	struct page	*page;
-	struct page	*cached_page = NULL;
-	struct pagevec	lru_pvec;
 	const struct iovec *cur_iov = iov; /* current iovec */
 	size_t		iov_offset = 0;	   /* offset in the current iovec */
 	char __user	*buf;
 
-	pagevec_init(&lru_pvec, 0);
-
 	/*
 	 * handle partial DIO write.  Adjust cur_iov if needed.
 	 */
@@ -1936,6 +1905,7 @@ generic_file_buffered_write(struct kiocb
 	}
 
 	do {
+		struct page *page;
 		pgoff_t index;		/* Pagecache index for current page */
 		unsigned long offset;	/* Offset into pagecache page */
 		unsigned long maxlen;	/* Bytes remaining in current iovec */
@@ -1962,7 +1932,8 @@ generic_file_buffered_write(struct kiocb
 		fault_in_pages_readable(buf, maxlen);
 #endif
 
-		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
+
+		page = __grab_cache_page(mapping, index);
 		if (!page) {
 			status = -ENOMEM;
 			break;
@@ -2030,9 +2001,6 @@ fs_write_aop_error:
 	} while (count);
 	*ppos = pos;
 
-	if (cached_page)
-		page_cache_release(cached_page);
-
 	/*
 	 * For now, when the user asks for O_SYNC, we'll actually give O_DSYNC
 	 */
@@ -2052,7 +2020,6 @@ fs_write_aop_error:
 	if (unlikely(file->f_flags & O_DIRECT) && written)
 		status = filemap_write_and_wait(mapping);
 
-	pagevec_lru_add(&lru_pvec);
 	return written ? written : status;
 }
 EXPORT_SYMBOL(generic_file_buffered_write);
fs/mpage.c +2 -8
Index: linux-2.6/fs/mpage.c
===================================================================
@@ -387,31 +387,25 @@ mpage_readpages(struct address_space *ma
 	struct bio *bio = NULL;
 	unsigned page_idx;
 	sector_t last_block_in_bio = 0;
-	struct pagevec lru_pvec;
 	struct buffer_head map_bh;
 	unsigned long first_logical_block = 0;
 
 	clear_buffer_mapped(&map_bh);
-	pagevec_init(&lru_pvec, 0);
 	for (page_idx = 0; page_idx < nr_pages; page_idx++) {
 		struct page *page = list_entry(pages->prev, struct page, lru);
 
 		prefetchw(&page->flags);
 		list_del(&page->lru);
-		if (!add_to_page_cache(page, mapping,
+		if (!add_to_page_cache_lru(page, mapping,
 					page->index, GFP_KERNEL)) {
 			bio = do_mpage_readpage(bio, page,
 					nr_pages - page_idx,
 					&last_block_in_bio, &map_bh,
 					&first_logical_block,
 					get_block);
-			if (!pagevec_add(&lru_pvec, page))
-				__pagevec_lru_add(&lru_pvec);
-		} else {
-			page_cache_release(page);
 		}
+		page_cache_release(page);
 	}
-	pagevec_lru_add(&lru_pvec);
 	BUG_ON(!list_empty(pages));
 	if (bio)
 		mpage_bio_submit(READ, bio);
mm/readahead.c +8 -16
Index: linux-2.6/mm/readahead.c
===================================================================
@@ -65,28 +65,25 @@ int read_cache_pages(struct address_spac
 			int (*filler)(void *, struct page *), void *data)
 {
 	struct page *page;
-	struct pagevec lru_pvec;
 	int ret = 0;
 
-	pagevec_init(&lru_pvec, 0);
-
 	while (!list_empty(pages)) {
 		page = list_to_page(pages);
 		list_del(&page->lru);
-		if (add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) {
+		if (add_to_page_cache_lru(page, mapping,
+					page->index, GFP_KERNEL)) {
 			page_cache_release(page);
 			continue;
 		}
+		page_cache_release(page);
+
 		ret = filler(data, page);
-		if (!pagevec_add(&lru_pvec, page))
-			__pagevec_lru_add(&lru_pvec);
-		if (ret) {
+		if (unlikely(ret)) {
 			put_pages_list(pages);
 			break;
 		}
 		task_io_account_read(PAGE_CACHE_SIZE);
 	}
-	pagevec_lru_add(&lru_pvec);
 	return ret;
 }
 
@@ -96,7 +93,6 @@ static int read_pages(struct address_spa
 		struct list_head *pages, unsigned nr_pages)
 {
 	unsigned page_idx;
-	struct pagevec lru_pvec;
 	int ret;
 
 	if (mapping->a_ops->readpages) {
@@ -106,19 +102,15 @@ static int read_pages(struct address_spa
 		goto out;
 	}
 
-	pagevec_init(&lru_pvec, 0);
 	for (page_idx = 0; page_idx < nr_pages; page_idx++) {
 		struct page *page = list_to_page(pages);
 		list_del(&page->lru);
-		if (!add_to_page_cache(page, mapping,
+		if (!add_to_page_cache_lru(page, mapping,
 					page->index, GFP_KERNEL)) {
 			mapping->a_ops->readpage(filp, page);
-			if (!pagevec_add(&lru_pvec, page))
-				__pagevec_lru_add(&lru_pvec);
-		} else
-			page_cache_release(page);
+		}
+		page_cache_release(page);
 	}
-	pagevec_lru_add(&lru_pvec);
 	ret = 0;
 out:
 	return ret;
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#8 lore raw

PATCH 8/41 mm: write iovec cleanup

In reply to [email protected]

Hide some of the open-coded nr_segs tests into the iovec helpers. This is all to simplify generic_file_buffered_write, because that gets more complex in the next patch.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c     |   36 +++++--------------
 mm/filemap.h     |  104 +++++++++++++++++++++++++++----------------------------
 mm/filemap_xip.c |   17 +++-----
 3 files changed, 69 insertions(+), 88 deletions(-)
mm/filemap.h +52 -52
Index: linux-2.6/mm/filemap.h
===================================================================
@@ -22,82 +22,82 @@ __filemap_copy_from_user_iovec_inatomic(
 
 /*
  * Copy as much as we can into the page and return the number of bytes which
- * were sucessfully copied.  If a fault is encountered then clear the page
- * out to (offset+bytes) and return the number of bytes which were copied.
- *
- * NOTE: For this to work reliably we really want copy_from_user_inatomic_nocache
- * to *NOT* zero any tail of the buffer that it failed to copy.  If it does,
- * and if the following non-atomic copy succeeds, then there is a small window
- * where the target page contains neither the data before the write, nor the
- * data after the write (it contains zero).  A read at this time will see
- * data that is inconsistent with any ordering of the read and the write.
- * (This has been detected in practice).
+ * were sucessfully copied.  If a fault is encountered then return the number of
+ * bytes which were copied.
  */
 static inline size_t
-filemap_copy_from_user(struct page *page, unsigned long offset,
-			const char __user *buf, unsigned bytes)
+filemap_copy_from_user_atomic(struct page *page, unsigned long offset,
+			const struct iovec *iov, unsigned long nr_segs,
+			size_t base, size_t bytes)
 {
 	char *kaddr;
-	int left;
+	size_t copied;
 
 	kaddr = kmap_atomic(page, KM_USER0);
-	left = __copy_from_user_inatomic_nocache(kaddr + offset, buf, bytes);
+	if (likely(nr_segs == 1)) {
+		int left;
+		char __user *buf = iov->iov_base + base;
+		left = __copy_from_user_inatomic_nocache(kaddr + offset,
+							buf, bytes);
+		copied = bytes - left;
+	} else {
+		copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset,
+							iov, base, bytes);
+	}
 	kunmap_atomic(kaddr, KM_USER0);
 
-	if (left != 0) {
-		/* Do it the slow way */
-		kaddr = kmap(page);
-		left = __copy_from_user_nocache(kaddr + offset, buf, bytes);
-		kunmap(page);
-	}
-	return bytes - left;
+	return copied;
 }
 
 /*
- * This has the same sideeffects and return value as filemap_copy_from_user().
- * The difference is that on a fault we need to memset the remainder of the
- * page (out to offset+bytes), to emulate filemap_copy_from_user()'s
- * single-segment behaviour.
+ * This has the same sideeffects and return value as
+ * filemap_copy_from_user_atomic().
+ * The difference is that it attempts to resolve faults.
  */
 static inline size_t
-filemap_copy_from_user_iovec(struct page *page, unsigned long offset,
-			const struct iovec *iov, size_t base, size_t bytes)
+filemap_copy_from_user(struct page *page, unsigned long offset,
+			const struct iovec *iov, unsigned long nr_segs,
+			 size_t base, size_t bytes)
 {
 	char *kaddr;
 	size_t copied;
 
-	kaddr = kmap_atomic(page, KM_USER0);
-	copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov,
-							 base, bytes);
-	kunmap_atomic(kaddr, KM_USER0);
-	if (copied != bytes) {
-		kaddr = kmap(page);
-		copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov,
-								 base, bytes);
-		if (bytes - copied)
-			memset(kaddr + offset + copied, 0, bytes - copied);
-		kunmap(page);
+	kaddr = kmap(page);
+	if (likely(nr_segs == 1)) {
+		int left;
+		char __user *buf = iov->iov_base + base;
+		left = __copy_from_user_nocache(kaddr + offset, buf, bytes);
+		copied = bytes - left;
+	} else {
+		copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset,
+							iov, base, bytes);
 	}
+	kunmap(page);
 	return copied;
 }
 
 static inline void
-filemap_set_next_iovec(const struct iovec **iovp, size_t *basep, size_t bytes)
+filemap_set_next_iovec(const struct iovec **iovp, unsigned long nr_segs,
+						 size_t *basep, size_t bytes)
 {
-	const struct iovec *iov = *iovp;
-	size_t base = *basep;
-
-	while (bytes) {
-		int copy = min(bytes, iov->iov_len - base);
-
-		bytes -= copy;
-		base += copy;
-		if (iov->iov_len == base) {
-			iov++;
-			base = 0;
+	if (likely(nr_segs == 1)) {
+		*basep += bytes;
+	} else {
+		const struct iovec *iov = *iovp;
+		size_t base = *basep;
+
+		while (bytes) {
+			int copy = min(bytes, iov->iov_len - base);
+
+			bytes -= copy;
+			base += copy;
+			if (iov->iov_len == base) {
+				iov++;
+				base = 0;
+			}
 		}
+		*iovp = iov;
+		*basep = base;
 	}
-	*iovp = iov;
-	*basep = base;
 }
 #endif
mm/filemap.c +10 -26
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1897,12 +1897,7 @@ generic_file_buffered_write(struct kiocb
 	/*
 	 * handle partial DIO write.  Adjust cur_iov if needed.
 	 */
-	if (likely(nr_segs == 1))
-		buf = iov->iov_base + written;
-	else {
-		filemap_set_next_iovec(&cur_iov, &iov_offset, written);
-		buf = cur_iov->iov_base + iov_offset;
-	}
+	filemap_set_next_iovec(&cur_iov, nr_segs, &iov_offset, written);
 
 	do {
 		struct page *page;
@@ -1912,6 +1907,7 @@ generic_file_buffered_write(struct kiocb
 		size_t bytes;		/* Bytes to write to page */
 		size_t copied;		/* Bytes copied from user */
 
+		buf = cur_iov->iov_base + iov_offset;
 		offset = (pos & (PAGE_CACHE_SIZE - 1));
 		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
@@ -1943,13 +1939,10 @@ generic_file_buffered_write(struct kiocb
 		if (unlikely(status))
 			goto fs_write_aop_error;
 
-		if (likely(nr_segs == 1))
-			copied = filemap_copy_from_user(page, offset,
-							buf, bytes);
-		else
-			copied = filemap_copy_from_user_iovec(page, offset,
-						cur_iov, iov_offset, bytes);
+		copied = filemap_copy_from_user(page, offset,
+					cur_iov, nr_segs, iov_offset, bytes);
 		flush_dcache_page(page);
+
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
 		if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
 			goto fs_write_aop_error;
@@ -1960,20 +1953,11 @@ generic_file_buffered_write(struct kiocb
 		if (unlikely(status > 0)) /* filesystem did partial write */
 			copied = status;
 
-		if (likely(copied > 0)) {
-			written += copied;
-			count -= copied;
-			pos += copied;
-			buf += copied;
-			if (unlikely(nr_segs > 1)) {
-				filemap_set_next_iovec(&cur_iov,
-						&iov_offset, copied);
-				if (count)
-					buf = cur_iov->iov_base + iov_offset;
-			} else {
-				iov_offset += copied;
-			}
-		}
+		written += copied;
+		count -= copied;
+		pos += copied;
+		filemap_set_next_iovec(&cur_iov, nr_segs, &iov_offset, copied);
+
 		unlock_page(page);
 		mark_page_accessed(page);
 		page_cache_release(page);
mm/filemap_xip.c +7 -10
Index: linux-2.6/mm/filemap_xip.c
===================================================================
@@ -15,7 +15,6 @@
 #include <linux/rmap.h>
 #include <linux/sched.h>
 #include <asm/tlbflush.h>
-#include "filemap.h"
 
 /*
  * We do use our own empty page to avoid interference with other users
@@ -319,6 +318,7 @@ __xip_file_write(struct file *filp, cons
 		unsigned long index;
 		unsigned long offset;
 		size_t copied;
+		char *kaddr;
 
 		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
 		index = pos >> PAGE_CACHE_SHIFT;
@@ -326,14 +326,6 @@ __xip_file_write(struct file *filp, cons
 		if (bytes > count)
 			bytes = count;
 
-		/*
-		 * Bring in the user page that we will copy from _first_.
-		 * Otherwise there's a nasty deadlock on copying from the
-		 * same page as we're writing to, without it being marked
-		 * up-to-date.
-		 */
-		fault_in_pages_readable(buf, bytes);
-
 		page = a_ops->get_xip_page(mapping,
 					   index*(PAGE_SIZE/512), 0);
 		if (IS_ERR(page) && (PTR_ERR(page) == -ENODATA)) {
@@ -350,8 +342,13 @@ __xip_file_write(struct file *filp, cons
 			break;
 		}
 
-		copied = filemap_copy_from_user(page, offset, buf, bytes);
+		fault_in_pages_readable(buf, bytes);
+		kaddr = kmap_atomic(page, KM_USER0);
+		copied = bytes -
+			__copy_from_user_inatomic_nocache(kaddr, buf, bytes);
+		kunmap_atomic(kaddr, KM_USER0);
 		flush_dcache_page(page);
+
 		if (likely(copied > 0)) {
 			status = copied;
 
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#9 lore raw

PATCH 9/41 mm: fix pagecache write deadlocks

In reply to [email protected]

Modify the core write() code so that it won't take a pagefault while holding a lock on the pagecache page. There are a number of different deadlocks possible if we try to do such a thing:

1. generic_buffered_write
2. lock_page
3. prepare_write
4. unlock_page+vmtruncate
5. copy_from_user
6. mmap_sem(r)
7. handle_mm_fault
8. lock_page (filemap_nopage)
9. commit_write
10. unlock_page

a. sys_munmap / sys_mlock / others
b. mmap_sem(w)
c. make_pages_present
d. get_user_pages
e. handle_mm_fault
f. lock_page (filemap_nopage)

2,8 - recursive deadlock if page is same
2,8;2,8 - ABBA deadlock is page is different
2,6;b,f - ABBA deadlock if page is same

The solution is as follows:
1. If we find the destination page is uptodate, continue as normal, but use atomic usercopies which do not take pagefaults and do not zero the uncopied tail of the destination. The destination is already uptodate, so we can commit_write the full length even if there was a partial copy: it does not matter that the tail was not modified, because if it is dirtied and written back to disk it will not cause any problems (uptodate *means* that the destination page is as new or newer than the copy on disk).

1a. The above requires that fault_in_pages_readable correctly returns access
information, because atomic usercopies cannot distinguish between non-present pages in a readable mapping, from lack of a readable mapping.

2. If we find the destination page is non uptodate, unlock it (this could be made slightly more optimal), then allocate a temporary page to copy the source data into. Relock the destination page and continue with the copy. However, instead of a usercopy (which might take a fault), copy the data from the pinned temporary page via the kernel address space.

(also, rename maxlen to seglen, because it was confusing)

This increases the CPU/memory copy cost by almost 50% on the affected workloads. That will be solved by introducing a new set of pagecache write aops in a subsequent patch.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 include/linux/pagemap.h |   11 +++-
 mm/filemap.c            |  122 ++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 112 insertions(+), 21 deletions(-)
mm/filemap.c +103 -19
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1900,11 +1900,12 @@ generic_file_buffered_write(struct kiocb
 	filemap_set_next_iovec(&cur_iov, nr_segs, &iov_offset, written);
 
 	do {
+		struct page *src_page;
 		struct page *page;
 		pgoff_t index;		/* Pagecache index for current page */
 		unsigned long offset;	/* Offset into pagecache page */
-		unsigned long maxlen;	/* Bytes remaining in current iovec */
-		size_t bytes;		/* Bytes to write to page */
+		unsigned long seglen;	/* Bytes remaining in current iovec */
+		unsigned long bytes;	/* Bytes to write to page */
 		size_t copied;		/* Bytes copied from user */
 
 		buf = cur_iov->iov_base + iov_offset;
@@ -1914,20 +1915,30 @@ generic_file_buffered_write(struct kiocb
 		if (bytes > count)
 			bytes = count;
 
-		maxlen = cur_iov->iov_len - iov_offset;
-		if (maxlen > bytes)
-			maxlen = bytes;
+		/*
+		 * a non-NULL src_page indicates that we're doing the
+		 * copy via get_user_pages and kmap.
+		 */
+		src_page = NULL;
+
+		seglen = cur_iov->iov_len - iov_offset;
+		if (seglen > bytes)
+			seglen = bytes;
 
-#ifndef CONFIG_DEBUG_VM
 		/*
 		 * Bring in the user page that we will copy from _first_.
 		 * Otherwise there's a nasty deadlock on copying from the
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
+		 *
+		 * Not only is this an optimisation, but it is also required
+		 * to check that the address is actually valid, when atomic
+		 * usercopies are used, below.
 		 */
-		fault_in_pages_readable(buf, maxlen);
-#endif
-
+		if (unlikely(fault_in_pages_readable(buf, seglen))) {
+			status = -EFAULT;
+			break;
+		}
 
 		page = __grab_cache_page(mapping, index);
 		if (!page) {
@@ -1935,32 +1946,104 @@ generic_file_buffered_write(struct kiocb
 			break;
 		}
 
+		/*
+		 * non-uptodate pages cannot cope with short copies, and we
+		 * cannot take a pagefault with the destination page locked.
+		 * So pin the source page to copy it.
+		 */
+		if (!PageUptodate(page)) {
+			unlock_page(page);
+
+			src_page = alloc_page(GFP_KERNEL);
+			if (!src_page) {
+				page_cache_release(page);
+				status = -ENOMEM;
+				break;
+			}
+
+			/*
+			 * Cannot get_user_pages with a page locked for the
+			 * same reason as we can't take a page fault with a
+			 * page locked (as explained below).
+			 */
+			copied = filemap_copy_from_user(src_page, offset,
+					cur_iov, nr_segs, iov_offset, bytes);
+			if (unlikely(copied == 0)) {
+				status = -EFAULT;
+				page_cache_release(page);
+				page_cache_release(src_page);
+				break;
+			}
+			bytes = copied;
+
+			lock_page(page);
+			/*
+			 * Can't handle the page going uptodate here, because
+			 * that means we would use non-atomic usercopies, which
+			 * zero out the tail of the page, which can cause
+			 * zeroes to become transiently visible. We could just
+			 * use a non-zeroing copy, but the APIs aren't too
+			 * consistent.
+			 */
+			if (unlikely(!page->mapping || PageUptodate(page))) {
+				unlock_page(page);
+				page_cache_release(page);
+				page_cache_release(src_page);
+				continue;
+			}
+
+		}
+
 		status = a_ops->prepare_write(file, page, offset, offset+bytes);
 		if (unlikely(status))
 			goto fs_write_aop_error;
 
-		copied = filemap_copy_from_user(page, offset,
+		if (!src_page) {
+			/*
+			 * Must not enter the pagefault handler here, because
+			 * we hold the page lock, so we might recursively
+			 * deadlock on the same lock, or get an ABBA deadlock
+			 * against a different lock, or against the mmap_sem
+			 * (which nests outside the page lock).  So increment
+			 * preempt count, and use _atomic usercopies.
+			 *
+			 * The page is uptodate so we are OK to encounter a
+			 * short copy: if unmodified parts of the page are
+			 * marked dirty and written out to disk, it doesn't
+			 * really matter.
+			 */
+			pagefault_disable();
+			copied = filemap_copy_from_user_atomic(page, offset,
 					cur_iov, nr_segs, iov_offset, bytes);
+			pagefault_enable();
+		} else {
+			void *src, *dst;
+			src = kmap_atomic(src_page, KM_USER0);
+			dst = kmap_atomic(page, KM_USER1);
+			memcpy(dst + offset, src + offset, bytes);
+			kunmap_atomic(dst, KM_USER1);
+			kunmap_atomic(src, KM_USER0);
+			copied = bytes;
+		}
 		flush_dcache_page(page);
 
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
 		if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
 			goto fs_write_aop_error;
-		if (unlikely(copied != bytes)) {
-			status = -EFAULT;
-			goto fs_write_aop_error;
-		}
 		if (unlikely(status > 0)) /* filesystem did partial write */
-			copied = status;
+			copied = min_t(size_t, copied, status);
+
+		unlock_page(page);
+		mark_page_accessed(page);
+		page_cache_release(page);
+		if (src_page)
+			page_cache_release(src_page);
 
 		written += copied;
 		count -= copied;
 		pos += copied;
 		filemap_set_next_iovec(&cur_iov, nr_segs, &iov_offset, copied);
 
-		unlock_page(page);
-		mark_page_accessed(page);
-		page_cache_release(page);
 		balance_dirty_pages_ratelimited(mapping);
 		cond_resched();
 		continue;
@@ -1969,6 +2052,8 @@ fs_write_aop_error:
 		if (status != AOP_TRUNCATED_PAGE)
 			unlock_page(page);
 		page_cache_release(page);
+		if (src_page)
+			page_cache_release(src_page);
 
 		/*
 		 * prepare_write() may have instantiated a few blocks
@@ -1981,7 +2066,6 @@ fs_write_aop_error:
 			continue;
 		else
 			break;
-
 	} while (count);
 	*ppos = pos;
 
include/linux/pagemap.h +9 -2
Index: linux-2.6/include/linux/pagemap.h
===================================================================
@@ -218,6 +218,9 @@ static inline int fault_in_pages_writeab
 {
 	int ret;
 
+	if (unlikely(size == 0))
+		return 0;
+
 	/*
 	 * Writing zeroes into userspace here is OK, because we know that if
 	 * the zero gets there, we'll be overwriting it.
@@ -237,19 +240,23 @@ static inline int fault_in_pages_writeab
 	return ret;
 }
 
-static inline void fault_in_pages_readable(const char __user *uaddr, int size)
+static inline int fault_in_pages_readable(const char __user *uaddr, int size)
 {
 	volatile char c;
 	int ret;
 
+	if (unlikely(size == 0))
+		return 0;
+
 	ret = __get_user(c, uaddr);
 	if (ret == 0) {
 		const char __user *end = uaddr + size - 1;
 
 		if (((unsigned long)uaddr & PAGE_MASK) !=
 				((unsigned long)end & PAGE_MASK))
-		 	__get_user(c, end);
+		 	ret = __get_user(c, end);
 	}
+	return ret;
 }
 
 #endif /* _LINUX_PAGEMAP_H */
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#10 lore raw

PATCH 10/41 mm: buffered write iterator

In reply to [email protected]

Add an iterator data structure to operate over an iovec. Add usercopy operators needed by generic_file_buffered_write, and convert that function over.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 include/linux/fs.h |   33 ++++++++++++
 mm/filemap.c       |  144 +++++++++++++++++++++++++++++++++++++++++++----------
 mm/filemap.h       |  103 -------------------------------------
 3 files changed, 150 insertions(+), 130 deletions(-)
include/linux/fs.h +33 -0
Index: linux-2.6/include/linux/fs.h
===================================================================
@@ -416,6 +416,39 @@ struct page;
 struct address_space;
 struct writeback_control;
 
+struct iov_iter {
+	const struct iovec *iov;
+	unsigned long nr_segs;
+	size_t iov_offset;
+	size_t count;
+};
+
+size_t iov_iter_copy_from_user_atomic(struct page *page,
+		struct iov_iter *i, unsigned long offset, size_t bytes);
+size_t iov_iter_copy_from_user(struct page *page,
+		struct iov_iter *i, unsigned long offset, size_t bytes);
+void iov_iter_advance(struct iov_iter *i, size_t bytes);
+int iov_iter_fault_in_readable(struct iov_iter *i);
+size_t iov_iter_single_seg_count(struct iov_iter *i);
+
+static inline void iov_iter_init(struct iov_iter *i,
+			const struct iovec *iov, unsigned long nr_segs,
+			size_t count, size_t written)
+{
+	i->iov = iov;
+	i->nr_segs = nr_segs;
+	i->iov_offset = 0;
+	i->count = count + written;
+
+	iov_iter_advance(i, written);
+}
+
+static inline size_t iov_iter_count(struct iov_iter *i)
+{
+	return i->count;
+}
+
+
 struct address_space_operations {
 	int (*writepage)(struct page *page, struct writeback_control *wbc);
 	int (*readpage)(struct file *, struct page *);
mm/filemap.c +117 -27

218 lines not on this page · raw message

mm/filemap.h deleted +0 -103

103 lines not on this page · raw message

Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#11 lore raw

PATCH 11/41 fs: fix data-loss on error

In reply to [email protected]

New buffers against uptodate pages are simply be marked uptodate, while the buffer_new bit remains set. This causes error-case code to zero out parts of those buffers because it thinks they contain stale data: wrong, they are actually uptodate so this is a data loss situation.

Fix this by actually clearning buffer_new and marking the buffer dirty. It makes sense to always clear buffer_new before setting a buffer uptodate.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 fs/buffer.c |    2 ++
 1 file changed, 2 insertions(+)
fs/buffer.c +2 -0
Index: linux-2.6/fs/buffer.c
===================================================================
@@ -1816,7 +1816,9 @@ static int __block_prepare_write(struct 
 				unmap_underlying_metadata(bh->b_bdev,
 							bh->b_blocknr);
 				if (PageUptodate(page)) {
+					clear_buffer_new(bh);
 					set_buffer_uptodate(bh);
+					mark_buffer_dirty(bh);
 					continue;
 				}
 				if (block_end > to || block_start < from) {
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#12 lore raw

PATCH 12/41 fs: introduce write_begin, write_end, and perform_write aops

In reply to [email protected]

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>

API design contributions, code review and fixes.

 Documentation/filesystems/Locking |    9 -
 Documentation/filesystems/vfs.txt |   45 +++++++
 drivers/block/loop.c              |   75 ++++-------
 fs/buffer.c                       |  198 ++++++++++++++++++++++++++-----
 fs/libfs.c                        |   44 +++++++
 fs/namei.c                        |   47 +------
 fs/revoked_inode.c                |   14 ++
 fs/splice.c                       |   70 +----------
 include/linux/buffer_head.h       |   10 +
 include/linux/fs.h                |   30 ++++
 include/linux/pagemap.h           |    2 
 mm/filemap.c                      |  238 +++++++++++++++++++++++++++++++++-----
 12 files changed, 576 insertions(+), 206 deletions(-)
include/linux/fs.h +29 -1
Index: linux-2.6/include/linux/fs.h
===================================================================
@@ -409,6 +409,8 @@ enum positive_aop_returns {
 	AOP_TRUNCATED_PAGE	= 0x80001,
 };
 
+#define AOP_FLAG_UNINTERRUPTIBLE	0x0001 /* will not do a short write */
+
 /*
  * oh the beauties of C type declarations.
  */
@@ -428,7 +430,7 @@ size_t iov_iter_copy_from_user_atomic(st
 size_t iov_iter_copy_from_user(struct page *page,
 		struct iov_iter *i, unsigned long offset, size_t bytes);
 void iov_iter_advance(struct iov_iter *i, size_t bytes);
-int iov_iter_fault_in_readable(struct iov_iter *i);
+int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
 size_t iov_iter_single_seg_count(struct iov_iter *i);
 
 static inline void iov_iter_init(struct iov_iter *i,
@@ -469,6 +471,14 @@ struct address_space_operations {
 	 */
 	int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
 	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
+
+	int (*write_begin)(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata);
+	int (*write_end)(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata);
+
 	/* Unfortunately this kludge is needed for FIBMAP. Don't use it */
 	sector_t (*bmap)(struct address_space *, sector_t);
 	void (*invalidatepage) (struct page *, unsigned long);
@@ -483,6 +493,18 @@ struct address_space_operations {
 	int (*launder_page) (struct page *);
 };
 
+/*
+ * pagecache_write_begin/pagecache_write_end must be used by general code
+ * to write into the pagecache.
+ */
+int pagecache_write_begin(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata);
+
+int pagecache_write_end(struct file *, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata);
+
 struct backing_dev_info;
 struct address_space {
 	struct inode		*host;		/* owner: inode, block_device */
@@ -1894,6 +1916,12 @@ extern int simple_prepare_write(struct f
 			unsigned offset, unsigned to);
 extern int simple_commit_write(struct file *file, struct page *page,
 				unsigned offset, unsigned to);
+extern int simple_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata);
+extern int simple_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata);
 
 extern struct dentry *simple_lookup(struct inode *, struct dentry *, struct nameidata *);
 extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *);
mm/filemap.c +211 -27

313 lines not on this page · raw message

fs/buffer.c +166 -32

222 lines not on this page · raw message

include/linux/buffer_head.h +10 -0
Index: linux-2.6/include/linux/buffer_head.h
===================================================================
@@ -203,6 +203,16 @@ void block_invalidatepage(struct page *p
 int block_write_full_page(struct page *page, get_block_t *get_block,
 				struct writeback_control *wbc);
 int block_read_full_page(struct page*, get_block_t*);
+int block_write_begin(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page **, void **, get_block_t*);
+int block_write_end(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page *, void *);
+int generic_write_end(struct file *, struct address_space *,
+				loff_t, unsigned, unsigned,
+				struct page *, void *);
+void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
 int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
 				loff_t *);
include/linux/pagemap.h +2 -0
Index: linux-2.6/include/linux/pagemap.h
===================================================================
@@ -96,6 +96,8 @@ unsigned find_get_pages_contig(struct ad
 unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index,
 			int tag, unsigned int nr_pages, struct page **pages);
 
+struct page *__grab_cache_page(struct address_space *mapping, pgoff_t index);
+
 /*
  * Returns locked page at given index in given cache, creating it if needed.
  */
fs/libfs.c +44 -0

62 lines not on this page · raw message

drivers/block/loop.c +29 -46

127 lines not on this page · raw message

fs/namei.c +12 -35

65 lines not on this page · raw message

fs/splice.c +8 -62

99 lines not on this page · raw message

Documentation/filesystems/Locking +6 -3
Index: linux-2.6/Documentation/filesystems/Locking
===================================================================
@@ -178,15 +178,18 @@ prototypes:
 locking rules:
 	All except set_page_dirty may block
 
-			BKL	PageLocked(page)
+			BKL	PageLocked(page)	i_sem
 writepage:		no	yes, unlocks (see below)
 readpage:		no	yes, unlocks
 sync_page:		no	maybe
 writepages:		no
 set_page_dirty		no	no
 readpages:		no
-prepare_write:		no	yes
-commit_write:		no	yes
+prepare_write:		no	yes			yes
+commit_write:		no	yes			yes
+write_begin:		no	locks the page		yes
+write_end:		no	yes, unlocks		yes
+perform_write:		no	n/a			yes
 bmap:			yes
 invalidatepage:		no	yes
 releasepage:		no	yes
Documentation/filesystems/vfs.txt +45 -0

57 lines not on this page · raw message

fs/revoked_inode.c +14 -0
Index: linux-2.6/fs/revoked_inode.c
===================================================================
@@ -384,6 +384,20 @@ static int revoked_commit_write(struct f
 	return -EIO;
 }
 
+static int revoked_write_begin(struct file *file, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata)
+{
+	return -EIO;
+}
+
+static int revoked_write_end(struct file *file, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
+{
+	return -EIO;
+}
+
 static ssize_t revoked_direct_IO(int rw, struct kiocb *iocb,
 				 const struct iovec *iov, loff_t offset,
 				 unsigned long nr_segs)
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
maintainer
#13 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 3 lines · On Fri, 25 May 2007 22:21:56 +1000 [email protected] wrote:

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

It doesn't like LTP's vmsplice01:

------------[ cut here ]------------
kernel BUG at fs/buffer.c:1829!
invalid opcode: 0000 [#1]
SMP 
Modules linked in:
CPU:    0
EIP:    0060:[<c01a2938>]    Not tainted VLI
EFLAGS: 00010206   (2.6.22-rc3-mm1 #1)

EIP is at __block_prepare_write+0x348/0x360
eax: 4000081d ebx: c176f10c ecx: 000001f0 edx: c176f10c
esi: df877670 edi: deeae3f0 ebp: de89fdc0 esp: de89fd60
ds: 007b es: 007b fs: 00d8 gs: 0033 ss: 0068
Process vmsplice01 (pid: 15759, ti=de89e000 task=c3ac1100 task.ti=de89e000)

Stack: c0140083 de89fd7c 00000046 00000000 c2fffe20 000001f0 c176f10c deeae3f0 
       c017c982 c01ee9b7 df877668 c01ee9b7 df877670 c345f300 df877684 de89fdcc 
       c01ee9eb c176f10c de89fdc4 c015c7ee deeae504 c176f10c df877670 deeae3f0 
Call Trace:
 [<c0103e1a>] show_trace_log_lvl+0x1a/0x30
 [<c0103ed8>] show_stack_log_lvl+0xa8/0xe0
 [<c01040f9>] show_registers+0x1e9/0x2f0
 [<c010430f>] die+0x10f/0x240
 [<c01044d1>] do_trap+0x91/0xc0
 [<c0104889>] do_invalid_op+0x89/0xa0
 [<c03f44ca>] error_code+0x72/0x78
 [<c01a29d9>] block_write_begin+0x49/0xd0
 [<c01c9a27>] ext3_write_begin+0xb7/0x190
 [<c015e77f>] pagecache_write_begin+0x4f/0x150
 [<c019f02b>] pipe_to_file+0x8b/0x140
 [<c019ea00>] __splice_from_pipe+0x70/0x250
 [<c019ec28>] splice_from_pipe+0x48/0x70
 [<c019eef4>] generic_file_splice_write+0x54/0x100
 [<c019e91f>] do_splice_from+0x5f/0x80
 [<c019fd84>] sys_splice+0x164/0x200
 [<c0102d8e>] sysenter_past_esp+0x5f/0x99
 =======================
INFO: lockdep is turned off.
Code: 49 c0 89 44 24 0c 89 7c 24 08 89 5c 24 04 c7 04 24 ac 2a 49 c0 e8 e9 ff f7 ff e8 b4 21 f6 ff 8b 4d f0 e9 a6 fe ff ff 0f 0b eb fe <0f> 0b eb fe 8d 74 26 00 0f 0b eb fe 0f 0b eb fe 90 8d b4 26 00 
EIP: [<c01a2938>] __block_prepare_write+0x348/0x360 SS:ESP 0068:de89fd60

That's

	BUG_ON(to > PAGE_CACHE_SIZE);

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#14 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 48 lines · Andrew Morton
Quoted 3 lines · On Fri, 25 May 2007 22:21:56 +1000 [email protected] wrote:

These are intended to replace prepare_write and commit_write with more flexible alternatives that are also able to avoid the buffered write deadlock problems efficiently (which prepare_write is unable to do).

It doesn't like LTP's vmsplice01:

------------[ cut here ]------------
kernel BUG at fs/buffer.c:1829!
invalid opcode: 0000 [#1]
SMP 
Modules linked in:
CPU:    0
EIP:    0060:[<c01a2938>]    Not tainted VLI
EFLAGS: 00010206   (2.6.22-rc3-mm1 #1)

EIP is at __block_prepare_write+0x348/0x360
eax: 4000081d ebx: c176f10c ecx: 000001f0 edx: c176f10c
esi: df877670 edi: deeae3f0 ebp: de89fdc0 esp: de89fd60
ds: 007b es: 007b fs: 00d8 gs: 0033 ss: 0068
Process vmsplice01 (pid: 15759, ti=de89e000 task=c3ac1100 task.ti=de89e000)

Stack: c0140083 de89fd7c 00000046 00000000 c2fffe20 000001f0 c176f10c deeae3f0 
       c017c982 c01ee9b7 df877668 c01ee9b7 df877670 c345f300 df877684 de89fdcc 
       c01ee9eb c176f10c de89fdc4 c015c7ee deeae504 c176f10c df877670 deeae3f0 
Call Trace:
 [<c0103e1a>] show_trace_log_lvl+0x1a/0x30
 [<c0103ed8>] show_stack_log_lvl+0xa8/0xe0
 [<c01040f9>] show_registers+0x1e9/0x2f0
 [<c010430f>] die+0x10f/0x240
 [<c01044d1>] do_trap+0x91/0xc0
 [<c0104889>] do_invalid_op+0x89/0xa0
 [<c03f44ca>] error_code+0x72/0x78
 [<c01a29d9>] block_write_begin+0x49/0xd0
 [<c01c9a27>] ext3_write_begin+0xb7/0x190
 [<c015e77f>] pagecache_write_begin+0x4f/0x150
 [<c019f02b>] pipe_to_file+0x8b/0x140
 [<c019ea00>] __splice_from_pipe+0x70/0x250
 [<c019ec28>] splice_from_pipe+0x48/0x70
 [<c019eef4>] generic_file_splice_write+0x54/0x100
 [<c019e91f>] do_splice_from+0x5f/0x80
 [<c019fd84>] sys_splice+0x164/0x200
 [<c0102d8e>] sysenter_past_esp+0x5f/0x99
 =======================
INFO: lockdep is turned off.
Code: 49 c0 89 44 24 0c 89 7c 24 08 89 5c 24 04 c7 04 24 ac 2a 49 c0 e8 e9 ff f7 ff e8 b4 21 f6 ff 8b 4d f0 e9 a6 fe ff ff 0f 0b eb fe <0f> 0b eb fe 8d 74 26 00 0f 0b eb fe 0f 0b eb fe 90 8d b4 26 00 
EIP: [<c01a2938>] __block_prepare_write+0x348/0x360 SS:ESP 0068:de89fd60
Quoted 3 lines

That's

	BUG_ON(to > PAGE_CACHE_SIZE);

Thanks. Hmm, sorry I didn't test splice much. Does this fix it?

---

fs/splice.c +2 -2
Index: linux-2.6/fs/splice.c
===================================================================
@@ -570,7 +570,7 @@ static int pipe_to_file(struct pipe_inod
 	if (this_len + offset > PAGE_CACHE_SIZE)
 		this_len = PAGE_CACHE_SIZE - offset;
 
-	ret = pagecache_write_begin(file, mapping, sd->pos, sd->len,
+	ret = pagecache_write_begin(file, mapping, sd->pos, this_len,
 				AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
 	if (unlikely(ret))
 		goto out;
@@ -587,7 +587,7 @@ static int pipe_to_file(struct pipe_inod
 		buf->ops->unmap(pipe, buf, src);
 	}
 
-	ret = pagecache_write_end(file, mapping, sd->pos, sd->len, sd->len, page, fsdata);
+	ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len, page, fsdata);
 
 out:
 

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#15 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 11 lines · On Thu, 31 May 2007 06:43:27 +0200 Nick Piggin <[email protected]> wrote:
Quoted 8 lines

INFO: lockdep is turned off.

Code: 49 c0 89 44 24 0c 89 7c 24 08 89 5c 24 04 c7 04 24 ac 2a 49 c0 e8 e9 ff f7 ff e8 b4 21 f6 ff 8b 4d f0 e9 a6 fe ff ff 0f 0b eb fe <0f> 0b eb fe 8d 74 26 00 0f 0b eb fe 0f 0b eb fe 90 8d b4 26 00 
EIP: [<c01a2938>] __block_prepare_write+0x348/0x360 SS:ESP 0068:de89fd60

That's

	BUG_ON(to > PAGE_CACHE_SIZE);

Thanks. Hmm, sorry I didn't test splice much. Does this fix it?

Don't know - I shelved the patches.

Given the great pile of build errors, I think we need the next rev.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#16 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 7 lines · Andrew Morton
Quoted 5 lines · On Thu, 31 May 2007 06:43:27 +0200 Nick Piggin <[email protected]> wrote:
Quoted 5 lines

INFO: lockdep is turned off.

Code: 49 c0 89 44 24 0c 89 7c 24 08 89 5c 24 04 c7 04 24 ac 2a 49 c0 e8 e9 ff f7 ff e8 b4 21 f6 ff 8b 4d f0 e9 a6 fe ff ff 0f 0b eb fe <0f> 0b eb fe 8d 74 26 00 0f 0b eb fe 0f 0b eb fe 90 8d b4 26 00 
EIP: [<c01a2938>] __block_prepare_write+0x348/0x360 SS:ESP 0068:de89fd60
Quoted 8 lines
Quoted 6 lines
Quoted 3 lines

That's

	BUG_ON(to > PAGE_CACHE_SIZE);

Thanks. Hmm, sorry I didn't test splice much. Does this fix it?

Don't know - I shelved the patches.

Oh, that didn't last long :P

Quoted 1 line

Given the great pile of build errors, I think we need the next rev.

I was working on bringing some of the others uptodate (hopefully before you did another release). There is not much point in doing that if they don't get merged because the patches just break again.

Were there build errors in any core code or converted filesystems? AFAIKS it was just in reiser4 and a couple of the "cont" filesystems that didn't get converted yet.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#17 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 3 lines · On Thu, 31 May 2007 06:57:54 +0200 Nick Piggin <[email protected]> wrote:
Quoted 1 line

Don't know - I shelved the patches.

Oh, that didn't last long :P

I have a heap of other stuff to get out the door. If I have to do just two bisects then it's 4AM and I give up then I have to repull everything and we're back to square one.

Fortunately, I didn't need to do a bisect this time. That's unusual.

Quoted 6 lines
Quoted 2 lines

Given the great pile of build errors, I think we need the next rev.

I was working on bringing some of the others uptodate (hopefully before you did another release). There is not much point in doing that if they don't get merged because the patches just break again.

There's not much point in sending build-busting patches either. Lots of people run allmodconfig.

My sympathy for broken patches is limited - you should see what happens over here ;)

I can do you a rollup with those patches reinstated after I've done rc3-mm1 if you like.

Quoted 3 lines

Were there build errors in any core code or converted filesystems? AFAIKS it was just in reiser4 and a couple of the "cont" filesystems that didn't get converted yet.

The _cont filesystems, reiser4 and that revoke warning.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#18 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 19 lines · Andrew Morton
Quoted 3 lines · On Thu, 31 May 2007 06:57:54 +0200 Nick Piggin <[email protected]> wrote:
Quoted 1 line

Don't know - I shelved the patches.

Oh, that didn't last long :P

I have a heap of other stuff to get out the door. If I have to do just two bisects then it's 4AM and I give up then I have to repull everything and we're back to square one.

Fortunately, I didn't need to do a bisect this time. That's unusual.

Quoted 6 lines
Quoted 2 lines

Given the great pile of build errors, I think we need the next rev.

I was working on bringing some of the others uptodate (hopefully before you did another release). There is not much point in doing that if they don't get merged because the patches just break again.

Quoted 8 lines

There's not much point in sending build-busting patches either. Lots of people run allmodconfig.

My sympathy for broken patches is limited - you should see what happens over here ;)

I can do you a rollup with those patches reinstated after I've done rc3-mm1 if you like.

OK, if you are planning to get a release out now, that's fair enough.

If you can send that rollup, it would be good. I could try getting everything to compile and do some more testing on it too.

Quoted 5 lines
Quoted 3 lines

Were there build errors in any core code or converted filesystems? AFAIKS it was just in reiser4 and a couple of the "cont" filesystems that didn't get converted yet.

The _cont filesystems, reiser4 and that revoke warning.

OK, thanks.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#19 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 2 lines · On Thu, 31 May 2007 07:15:39 +0200 Nick Piggin <[email protected]> wrote:

If you can send that rollup, it would be good. I could try getting everything to compile and do some more testing on it too.

Single patch against 2.6.22-rc3: http://userweb.kernel.org/~akpm/np.gz

broken-out: ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/mm/broken-out-2007-05-30-09-30.tar.gz

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#20 lore raw

Re: [patch 12/41] fs: introduce write_begin, write_end, and perform_write aops

Quoted 9 lines · Andrew Morton
Quoted 2 lines · On Thu, 31 May 2007 07:15:39 +0200 Nick Piggin <[email protected]> wrote:

If you can send that rollup, it would be good. I could try getting everything to compile and do some more testing on it too.

Single patch against 2.6.22-rc3: http://userweb.kernel.org/~akpm/np.gz

broken-out: ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/mm/broken-out-2007-05-30-09-30.tar.gz

Thanks, I'll get onto it.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#21 lore raw

PATCH 13/41 mm: restore KERNEL_DS optimisations

In reply to [email protected]

Restore the KERNEL_DS optimisation, especially helpful to the 2copy write path.

This may be a pretty questionable gain in most cases, especially after the legacy 2copy write path is removed, but it doesn't cost much.

Cc Linux Memory Management <[email protected]>
Cc Linux Filesystems <[email protected]>
 mm/filemap.c |   11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
mm/filemap.c +9 -2
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -2123,7 +2123,7 @@ static ssize_t generic_perform_write_2co
 		 * cannot take a pagefault with the destination page locked.
 		 * So pin the source page to copy it.
 		 */
-		if (!PageUptodate(page)) {
+		if (!PageUptodate(page) && !segment_eq(get_fs(), KERNEL_DS)) {
 			unlock_page(page);
 
 			src_page = alloc_page(GFP_KERNEL);
@@ -2248,6 +2248,13 @@ static ssize_t generic_perform_write(str
 	const struct address_space_operations *a_ops = mapping->a_ops;
 	long status = 0;
 	ssize_t written = 0;
+	unsigned int flags = 0;
+
+	/*
+	 * Copies from kernel address space cannot fail (NFSD is a big user).
+	 */
+	if (segment_eq(get_fs(), KERNEL_DS))
+		flags |= AOP_FLAG_UNINTERRUPTIBLE;
 
 	do {
 		struct page *page;
@@ -2279,7 +2286,7 @@ again:
 			break;
 		}
 
-		status = a_ops->write_begin(file, mapping, pos, bytes, 0,
+		status = a_ops->write_begin(file, mapping, pos, bytes, flags,
 						&page, &fsdata);
 		if (unlikely(status))
 			break;
Signature · 5 lines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#22 lore raw

PATCH 14/41 implement simple fs aops

In reply to [email protected]

Implement new aops for some of the simpler filesystems.

Cc Linux Filesystems <[email protected]>
 fs/configfs/inode.c   |    4 ++--
 fs/hugetlbfs/inode.c  |   16 ++++++++++------
 fs/ramfs/file-mmu.c   |    4 ++--
 fs/ramfs/file-nommu.c |    4 ++--
 fs/sysfs/inode.c      |    4 ++--
 mm/shmem.c            |   35 ++++++++++++++++++++++++++++-------
 6 files changed, 46 insertions(+), 21 deletions(-)
mm/shmem.c +28 -7
Index: linux-2.6/mm/shmem.c
===================================================================
@@ -1107,7 +1107,7 @@ static int shmem_getpage(struct inode *i
 	 * Normally, filepage is NULL on entry, and either found
 	 * uptodate immediately, or allocated and zeroed, or read
 	 * in under swappage, which is then assigned to filepage.
-	 * But shmem_prepare_write passes in a locked filepage,
+	 * But shmem_write_begin passes in a locked filepage,
 	 * which may be found not uptodate by other callers too,
 	 * and may need to be copied from the swappage read in.
 	 */
@@ -1452,14 +1452,35 @@ static const struct inode_operations shm
 static const struct inode_operations shmem_symlink_inline_operations;
 
 /*
- * Normally tmpfs makes no use of shmem_prepare_write, but it
+ * Normally tmpfs makes no use of shmem_write_begin, but it
  * lets a tmpfs file be used read-write below the loop driver.
  */
 static int
-shmem_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+shmem_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	struct inode *inode = mapping->host;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	*pagep = NULL;
+	return shmem_getpage(inode, index, pagep, SGP_WRITE, NULL);
+}
+
+static int
+shmem_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
-	struct inode *inode = page->mapping->host;
-	return shmem_getpage(inode, page->index, &page, SGP_WRITE, NULL);
+	struct inode *inode = mapping->host;
+
+	set_page_dirty(page);
+	mark_page_accessed(page);
+	page_cache_release(page);
+
+	if (pos+copied > inode->i_size)
+		i_size_write(inode, pos+copied);
+
+	return copied;
 }
 
 static ssize_t
@@ -2353,8 +2374,8 @@ static const struct address_space_operat
 	.writepage	= shmem_writepage,
 	.set_page_dirty	= __set_page_dirty_no_writeback,
 #ifdef CONFIG_TMPFS
-	.prepare_write	= shmem_prepare_write,
-	.commit_write	= simple_commit_write,
+	.write_begin	= shmem_write_begin,
+	.write_end	= shmem_write_end,
 #endif
 	.migratepage	= migrate_page,
 };
fs/configfs/inode.c +2 -2
Index: linux-2.6/fs/configfs/inode.c
===================================================================
@@ -41,8 +41,8 @@ extern struct super_block * configfs_sb;
 
 static const struct address_space_operations configfs_aops = {
 	.readpage	= simple_readpage,
-	.prepare_write	= simple_prepare_write,
-	.commit_write	= simple_commit_write
+	.write_begin	= simple_write_begin,
+	.write_end	= simple_write_end,
 };
 
 static struct backing_dev_info configfs_backing_dev_info = {
fs/sysfs/inode.c +2 -2
Index: linux-2.6/fs/sysfs/inode.c
===================================================================
@@ -21,8 +21,8 @@ extern struct super_block * sysfs_sb;
 
 static const struct address_space_operations sysfs_aops = {
 	.readpage	= simple_readpage,
-	.prepare_write	= simple_prepare_write,
-	.commit_write	= simple_commit_write
+	.write_begin	= simple_write_begin,
+	.write_end	= simple_write_end,
 };
 
 static struct backing_dev_info sysfs_backing_dev_info = {
fs/ramfs/file-mmu.c +2 -2
Index: linux-2.6/fs/ramfs/file-mmu.c
===================================================================
@@ -29,8 +29,8 @@
 
 const struct address_space_operations ramfs_aops = {
 	.readpage	= simple_readpage,
-	.prepare_write	= simple_prepare_write,
-	.commit_write	= simple_commit_write,
+	.write_begin	= simple_write_begin,
+	.write_end	= simple_write_end,
 	.set_page_dirty = __set_page_dirty_no_writeback,
 };
 
fs/ramfs/file-nommu.c +2 -2
Index: linux-2.6/fs/ramfs/file-nommu.c
===================================================================
@@ -29,8 +29,8 @@ static int ramfs_nommu_setattr(struct de
 
 const struct address_space_operations ramfs_aops = {
 	.readpage		= simple_readpage,
-	.prepare_write		= simple_prepare_write,
-	.commit_write		= simple_commit_write,
+	.write_begin		= simple_write_begin,
+	.write_end		= simple_write_end,
 	.set_page_dirty		= __set_page_dirty_no_writeback,
 };
 
fs/hugetlbfs/inode.c +10 -6
Index: linux-2.6/fs/hugetlbfs/inode.c
===================================================================
@@ -166,15 +166,19 @@ static int hugetlbfs_readpage(struct fil
 	return -EINVAL;
 }
 
-static int hugetlbfs_prepare_write(struct file *file,
-			struct page *page, unsigned offset, unsigned to)
+static int hugetlbfs_write_begin(struct file *file,
+			struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
 	return -EINVAL;
 }
 
-static int hugetlbfs_commit_write(struct file *file,
-			struct page *page, unsigned offset, unsigned to)
+static int hugetlbfs_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
+	BUG();
 	return -EINVAL;
 }
 
@@ -546,8 +550,8 @@ static void hugetlbfs_destroy_inode(stru
 
 static const struct address_space_operations hugetlbfs_aops = {
 	.readpage	= hugetlbfs_readpage,
-	.prepare_write	= hugetlbfs_prepare_write,
-	.commit_write	= hugetlbfs_commit_write,
+	.write_begin	= hugetlbfs_write_begin,
+	.write_end	= hugetlbfs_write_end,
 	.set_page_dirty	= hugetlbfs_set_page_dirty,
 };
 
#23 lore raw

PATCH 15/41 block_dev convert to new aops.

In reply to [email protected]
Cc Linux Filesystems <[email protected]>
 fs/block_dev.c |   26 +++++++++++++++++++-------
 1 file changed, 19 insertions(+), 7 deletions(-)
fs/block_dev.c +19 -7
Index: linux-2.6/fs/block_dev.c
===================================================================
@@ -380,14 +380,26 @@ static int blkdev_readpage(struct file *
 	return block_read_full_page(page, blkdev_get_block);
 }
 
-static int blkdev_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
-{
-	return block_prepare_write(page, from, to, blkdev_get_block);
+static int blkdev_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				blkdev_get_block);
 }
 
-static int blkdev_commit_write(struct file *file, struct page *page, unsigned from, unsigned to)
+static int blkdev_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
-	return block_commit_write(page, from, to);
+	int ret;
+	ret = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+
+	unlock_page(page);
+	page_cache_release(page);
+
+	return ret;
 }
 
 /*
@@ -1331,8 +1343,8 @@ const struct address_space_operations de
 	.readpage	= blkdev_readpage,
 	.writepage	= blkdev_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= blkdev_prepare_write,
-	.commit_write	= blkdev_commit_write,
+	.write_begin	= blkdev_write_begin,
+	.write_end	= blkdev_write_end,
 	.writepages	= generic_writepages,
 	.direct_IO	= blkdev_direct_IO,
 };
#24 lore raw

PATCH 16/41 ext2 convert to new aops.

In reply to [email protected]
 fs/ext2/dir.c   |   56 ++++++++++++++++++++++++++++++++++++++------------------
 fs/ext2/ext2.h  |    3 +++
 fs/ext2/inode.c |   24 +++++++++++++-----------
 3 files changed, 54 insertions(+), 29 deletions(-)
fs/ext2/inode.c +13 -11
Index: linux-2.6/fs/ext2/inode.c
===================================================================
@@ -726,18 +726,21 @@ ext2_readpages(struct file *file, struct
 	return mpage_readpages(mapping, pages, nr_pages, ext2_get_block);
 }
 
-static int
-ext2_prepare_write(struct file *file, struct page *page,
-			unsigned from, unsigned to)
+int __ext2_write_begin(struct file *file, struct address_space *mapping,
+		loff_t pos, unsigned len, unsigned flags,
+		struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page,from,to,ext2_get_block);
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+							ext2_get_block);
 }
 
 static int
-ext2_nobh_prepare_write(struct file *file, struct page *page,
-			unsigned from, unsigned to)
+ext2_write_begin(struct file *file, struct address_space *mapping,
+		loff_t pos, unsigned len, unsigned flags,
+		struct page **pagep, void **fsdata)
 {
-	return nobh_prepare_write(page,from,to,ext2_get_block);
+	*pagep = NULL;
+	return __ext2_write_begin(file, mapping, pos, len, flags, pagep,fsdata);
 }
 
 static int ext2_nobh_writepage(struct page *page,
@@ -773,8 +776,8 @@ const struct address_space_operations ex
 	.readpages		= ext2_readpages,
 	.writepage		= ext2_writepage,
 	.sync_page		= block_sync_page,
-	.prepare_write		= ext2_prepare_write,
-	.commit_write		= generic_commit_write,
+	.write_begin		= ext2_write_begin,
+	.write_end		= generic_write_end,
 	.bmap			= ext2_bmap,
 	.direct_IO		= ext2_direct_IO,
 	.writepages		= ext2_writepages,
@@ -791,8 +794,7 @@ const struct address_space_operations ex
 	.readpages		= ext2_readpages,
 	.writepage		= ext2_nobh_writepage,
 	.sync_page		= block_sync_page,
-	.prepare_write		= ext2_nobh_prepare_write,
-	.commit_write		= nobh_commit_write,
+	/* XXX: todo */
 	.bmap			= ext2_bmap,
 	.direct_IO		= ext2_direct_IO,
 	.writepages		= ext2_writepages,
fs/ext2/dir.c +38 -18

129 lines not on this page · raw message

fs/ext2/ext2.h +3 -0
Index: linux-2.6/fs/ext2/ext2.h
===================================================================
@@ -134,6 +134,9 @@ extern void ext2_truncate (struct inode 
 extern int ext2_setattr (struct dentry *, struct iattr *);
 extern void ext2_set_inode_flags(struct inode *inode);
 extern void ext2_get_inode_flags(struct ext2_inode_info *);
+int __ext2_write_begin(struct file *file, struct address_space *mapping,
+		loff_t pos, unsigned len, unsigned flags,
+		struct page **pagep, void **fsdata);
 
 /* ioctl.c */
 extern int ext2_ioctl (struct inode *, struct file *, unsigned int,
#25 lore raw

PATCH 17/41 ext3 convert to new aops.

In reply to [email protected]

Various fixes and improvements

 fs/ext3/inode.c |  136 ++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 88 insertions(+), 48 deletions(-)
fs/ext3/inode.c +88 -48
Index: linux-2.6/fs/ext3/inode.c
===================================================================
@@ -1147,51 +1147,68 @@ static int do_journal_get_write_access(h
 	return ext3_journal_get_write_access(handle, bh);
 }
 
-static int ext3_prepare_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
+static int ext3_write_begin(struct file *file, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata)
 {
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = mapping->host;
 	int ret, needed_blocks = ext3_writepage_trans_blocks(inode);
 	handle_t *handle;
 	int retries = 0;
+	struct page *page;
+	pgoff_t index;
+	unsigned from, to;
+
+	index = pos >> PAGE_CACHE_SHIFT;
+	from = pos & (PAGE_CACHE_SIZE - 1);
+	to = from + len;
 
 retry:
+	page = __grab_cache_page(mapping, index);
+	if (!page)
+		return -ENOMEM;
+	*pagep = page;
+
 	handle = ext3_journal_start(inode, needed_blocks);
 	if (IS_ERR(handle)) {
+		unlock_page(page);
+		page_cache_release(page);
 		ret = PTR_ERR(handle);
 		goto out;
 	}
-	if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
-		ret = nobh_prepare_write(page, from, to, ext3_get_block);
-	else
-		ret = block_prepare_write(page, from, to, ext3_get_block);
+	ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+							ext3_get_block);
 	if (ret)
-		goto prepare_write_failed;
+		goto write_begin_failed;
 
 	if (ext3_should_journal_data(inode)) {
 		ret = walk_page_buffers(handle, page_buffers(page),
 				from, to, NULL, do_journal_get_write_access);
 	}
-prepare_write_failed:
-	if (ret)
+write_begin_failed:
+	if (ret) {
 		ext3_journal_stop(handle);
+		unlock_page(page);
+		page_cache_release(page);
+	}
 	if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
 		goto retry;
 out:
 	return ret;
 }
 
+
 int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh)
 {
 	int err = journal_dirty_data(handle, bh);
 	if (err)
 		ext3_journal_abort_handle(__FUNCTION__, __FUNCTION__,
-						bh, handle,err);
+						bh, handle, err);
 	return err;
 }
 
-/* For commit_write() in data=journal mode */
-static int commit_write_fn(handle_t *handle, struct buffer_head *bh)
+/* For write_end() in data=journal mode */
+static int write_end_fn(handle_t *handle, struct buffer_head *bh)
 {
 	if (!buffer_mapped(bh) || buffer_freed(bh))
 		return 0;
@@ -1206,78 +1223,100 @@ static int commit_write_fn(handle_t *han
  * ext3 never places buffers on inode->i_mapping->private_list.  metadata
  * buffers are managed internally.
  */
-static int ext3_ordered_commit_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int ext3_ordered_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext3_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = file->f_mapping->host;
+	unsigned from, to;
 	int ret = 0, ret2;
 
+	from = pos & (PAGE_CACHE_SIZE - 1);
+	to = from + len;
+
 	ret = walk_page_buffers(handle, page_buffers(page),
 		from, to, NULL, ext3_journal_dirty_data);
 
 	if (ret == 0) {
 		/*
-		 * generic_commit_write() will run mark_inode_dirty() if i_size
+		 * generic_write_end() will run mark_inode_dirty() if i_size
 		 * changes.  So let's piggyback the i_disksize mark_inode_dirty
 		 * into that.
 		 */
 		loff_t new_i_size;
 
-		new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+		new_i_size = pos + copied;
 		if (new_i_size > EXT3_I(inode)->i_disksize)
 			EXT3_I(inode)->i_disksize = new_i_size;
-		ret = generic_commit_write(file, page, from, to);
+		copied = generic_write_end(file, mapping, pos, len, copied,
+							page, fsdata);
+		if (copied < 0)
+			ret = copied;
+	} else {
+		unlock_page(page);
+		page_cache_release(page);
 	}
 	ret2 = ext3_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
-static int ext3_writeback_commit_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int ext3_writeback_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext3_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = file->f_mapping->host;
 	int ret = 0, ret2;
 	loff_t new_i_size;
 
-	new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	new_i_size = pos + copied;
 	if (new_i_size > EXT3_I(inode)->i_disksize)
 		EXT3_I(inode)->i_disksize = new_i_size;
 
-	if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode))
-		ret = nobh_commit_write(file, page, from, to);
-	else
-		ret = generic_commit_write(file, page, from, to);
+	copied = generic_write_end(file, mapping, pos, len, copied,
+							page, fsdata);
+	if (copied < 0)
+		ret = copied;
 
 	ret2 = ext3_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
-static int ext3_journalled_commit_write(struct file *file,
-			struct page *page, unsigned from, unsigned to)
+static int ext3_journalled_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext3_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = mapping->host;
 	int ret = 0, ret2;
 	int partial = 0;
-	loff_t pos;
+	unsigned from, to;
 
-	/*
-	 * Here we duplicate the generic_commit_write() functionality
-	 */
-	pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	from = pos & (PAGE_CACHE_SIZE - 1);
+	to = from + len;
+
+	if (copied < len) {
+		if (!PageUptodate(page))
+			copied = 0;
+		page_zero_new_buffers(page, from+copied, to);
+	}
 
 	ret = walk_page_buffers(handle, page_buffers(page), from,
-				to, &partial, commit_write_fn);
+				to, &partial, write_end_fn);
 	if (!partial)
 		SetPageUptodate(page);
-	if (pos > inode->i_size)
-		i_size_write(inode, pos);
+	unlock_page(page);
+	page_cache_release(page);
+	if (pos+copied > inode->i_size)
+		i_size_write(inode, pos+copied);
 	EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
 	if (inode->i_size > EXT3_I(inode)->i_disksize) {
 		EXT3_I(inode)->i_disksize = inode->i_size;
@@ -1285,10 +1324,11 @@ static int ext3_journalled_commit_write(
 		if (!ret)
 			ret = ret2;
 	}
+
 	ret2 = ext3_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
 /*
@@ -1546,7 +1586,7 @@ static int ext3_journalled_writepage(str
 			PAGE_CACHE_SIZE, NULL, do_journal_get_write_access);
 
 		err = walk_page_buffers(handle, page_buffers(page), 0,
-				PAGE_CACHE_SIZE, NULL, commit_write_fn);
+				PAGE_CACHE_SIZE, NULL, write_end_fn);
 		if (ret == 0)
 			ret = err;
 		EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
@@ -1706,8 +1746,8 @@ static const struct address_space_operat
 	.readpages	= ext3_readpages,
 	.writepage	= ext3_ordered_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext3_prepare_write,
-	.commit_write	= ext3_ordered_commit_write,
+	.write_begin	= ext3_write_begin,
+	.write_end	= ext3_ordered_write_end,
 	.bmap		= ext3_bmap,
 	.invalidatepage	= ext3_invalidatepage,
 	.releasepage	= ext3_releasepage,
@@ -1720,8 +1760,8 @@ static const struct address_space_operat
 	.readpages	= ext3_readpages,
 	.writepage	= ext3_writeback_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext3_prepare_write,
-	.commit_write	= ext3_writeback_commit_write,
+	.write_begin	= ext3_write_begin,
+	.write_end	= ext3_writeback_write_end,
 	.bmap		= ext3_bmap,
 	.invalidatepage	= ext3_invalidatepage,
 	.releasepage	= ext3_releasepage,
@@ -1734,8 +1774,8 @@ static const struct address_space_operat
 	.readpages	= ext3_readpages,
 	.writepage	= ext3_journalled_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext3_prepare_write,
-	.commit_write	= ext3_journalled_commit_write,
+	.write_begin	= ext3_write_begin,
+	.write_end	= ext3_journalled_write_end,
 	.set_page_dirty	= ext3_journalled_set_page_dirty,
 	.bmap		= ext3_bmap,
 	.invalidatepage	= ext3_invalidatepage,
#26 lore raw

PATCH 18/41 ext4 convert to new aops.

In reply to [email protected]

Cc: [email protected]
Cc: Linux Filesystems <[email protected]> Convert ext4 to use write_begin()/write_end() methods.

 fs/ext4/inode.c |  147 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 93 insertions(+), 54 deletions(-)
fs/ext4/inode.c +93 -54
Index: linux-2.6/fs/ext4/inode.c
===================================================================
@@ -1146,34 +1146,50 @@ static int do_journal_get_write_access(h
 	return ext4_journal_get_write_access(handle, bh);
 }
 
-static int ext4_prepare_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
+static int ext4_write_begin(struct file *file, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata)
 {
-	struct inode *inode = page->mapping->host;
+ 	struct inode *inode = mapping->host;
 	int ret, needed_blocks = ext4_writepage_trans_blocks(inode);
 	handle_t *handle;
 	int retries = 0;
+ 	struct page *page;
+ 	pgoff_t index;
+ 	unsigned from, to;
+
+ 	index = pos >> PAGE_CACHE_SHIFT;
+ 	from = pos & (PAGE_CACHE_SIZE - 1);
+ 	to = from + len;
 
 retry:
-	handle = ext4_journal_start(inode, needed_blocks);
-	if (IS_ERR(handle)) {
-		ret = PTR_ERR(handle);
-		goto out;
+ 	page = __grab_cache_page(mapping, index);
+ 	if (!page)
+ 		return -ENOMEM;
+ 	*pagep = page;
+
+  	handle = ext4_journal_start(inode, needed_blocks);
+  	if (IS_ERR(handle)) {
+ 		unlock_page(page);
+ 		page_cache_release(page);
+  		ret = PTR_ERR(handle);
+  		goto out;
 	}
-	if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
-		ret = nobh_prepare_write(page, from, to, ext4_get_block);
-	else
-		ret = block_prepare_write(page, from, to, ext4_get_block);
-	if (ret)
-		goto prepare_write_failed;
 
-	if (ext4_should_journal_data(inode)) {
+	ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+							ext4_get_block);
+
+	if (!ret && ext4_should_journal_data(inode)) {
 		ret = walk_page_buffers(handle, page_buffers(page),
 				from, to, NULL, do_journal_get_write_access);
 	}
-prepare_write_failed:
-	if (ret)
+
+	if (ret) {
 		ext4_journal_stop(handle);
+ 		unlock_page(page);
+ 		page_cache_release(page);
+	}
+
 	if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
 		goto retry;
 out:
@@ -1185,12 +1201,12 @@ int ext4_journal_dirty_data(handle_t *ha
 	int err = jbd2_journal_dirty_data(handle, bh);
 	if (err)
 		ext4_journal_abort_handle(__FUNCTION__, __FUNCTION__,
-						bh, handle,err);
+						bh, handle, err);
 	return err;
 }
 
-/* For commit_write() in data=journal mode */
-static int commit_write_fn(handle_t *handle, struct buffer_head *bh)
+/* For write_end() in data=journal mode */
+static int write_end_fn(handle_t *handle, struct buffer_head *bh)
 {
 	if (!buffer_mapped(bh) || buffer_freed(bh))
 		return 0;
@@ -1205,78 +1221,100 @@ static int commit_write_fn(handle_t *han
  * ext4 never places buffers on inode->i_mapping->private_list.  metadata
  * buffers are managed internally.
  */
-static int ext4_ordered_commit_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int ext4_ordered_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext4_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = file->f_mapping->host;
+	unsigned from, to;
 	int ret = 0, ret2;
 
+	from = pos & (PAGE_CACHE_SIZE - 1);
+	to = from + len;
+
 	ret = walk_page_buffers(handle, page_buffers(page),
 		from, to, NULL, ext4_journal_dirty_data);
 
 	if (ret == 0) {
 		/*
-		 * generic_commit_write() will run mark_inode_dirty() if i_size
+		 * generic_write_end() will run mark_inode_dirty() if i_size
 		 * changes.  So let's piggyback the i_disksize mark_inode_dirty
 		 * into that.
 		 */
 		loff_t new_i_size;
 
-		new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+		new_i_size = pos + copied;
 		if (new_i_size > EXT4_I(inode)->i_disksize)
 			EXT4_I(inode)->i_disksize = new_i_size;
-		ret = generic_commit_write(file, page, from, to);
+		copied = generic_write_end(file, mapping, pos, len, copied,
+							page, fsdata);
+		if (copied < 0)
+			ret = copied;
+	} else {
+		unlock_page(page);
+		page_cache_release(page);
 	}
 	ret2 = ext4_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
-static int ext4_writeback_commit_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int ext4_writeback_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext4_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = file->f_mapping->host;
 	int ret = 0, ret2;
 	loff_t new_i_size;
 
-	new_i_size = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	new_i_size = pos + copied;
 	if (new_i_size > EXT4_I(inode)->i_disksize)
 		EXT4_I(inode)->i_disksize = new_i_size;
 
-	if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
-		ret = nobh_commit_write(file, page, from, to);
-	else
-		ret = generic_commit_write(file, page, from, to);
+	copied = generic_write_end(file, mapping, pos, len, copied,
+							page, fsdata);
+	if (copied < 0)
+		ret = copied;
 
 	ret2 = ext4_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
-static int ext4_journalled_commit_write(struct file *file,
-			struct page *page, unsigned from, unsigned to)
+static int ext4_journalled_write_end(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned copied,
+				struct page *page, void *fsdata)
 {
 	handle_t *handle = ext4_journal_current_handle();
-	struct inode *inode = page->mapping->host;
+	struct inode *inode = mapping->host;
 	int ret = 0, ret2;
 	int partial = 0;
-	loff_t pos;
+	unsigned from, to;
 
-	/*
-	 * Here we duplicate the generic_commit_write() functionality
-	 */
-	pos = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
+	from = pos & (PAGE_CACHE_SIZE - 1);
+	to = from + len;
+
+	if (copied < len) {
+		if (!PageUptodate(page))
+			copied = 0;
+		page_zero_new_buffers(page, from+copied, to);
+	}
 
 	ret = walk_page_buffers(handle, page_buffers(page), from,
-				to, &partial, commit_write_fn);
+				to, &partial, write_end_fn);
 	if (!partial)
 		SetPageUptodate(page);
-	if (pos > inode->i_size)
-		i_size_write(inode, pos);
+	unlock_page(page);
+	page_cache_release(page);
+	if (pos+copied > inode->i_size)
+		i_size_write(inode, pos+copied);
 	EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
 	if (inode->i_size > EXT4_I(inode)->i_disksize) {
 		EXT4_I(inode)->i_disksize = inode->i_size;
@@ -1284,10 +1322,11 @@ static int ext4_journalled_commit_write(
 		if (!ret)
 			ret = ret2;
 	}
+
 	ret2 = ext4_journal_stop(handle);
 	if (!ret)
 		ret = ret2;
-	return ret;
+	return ret ? ret : copied;
 }
 
 /*
@@ -1545,7 +1584,7 @@ static int ext4_journalled_writepage(str
 			PAGE_CACHE_SIZE, NULL, do_journal_get_write_access);
 
 		err = walk_page_buffers(handle, page_buffers(page), 0,
-				PAGE_CACHE_SIZE, NULL, commit_write_fn);
+				PAGE_CACHE_SIZE, NULL, write_end_fn);
 		if (ret == 0)
 			ret = err;
 		EXT4_I(inode)->i_state |= EXT4_STATE_JDATA;
@@ -1705,8 +1744,8 @@ static const struct address_space_operat
 	.readpages	= ext4_readpages,
 	.writepage	= ext4_ordered_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext4_prepare_write,
-	.commit_write	= ext4_ordered_commit_write,
+	.write_begin	= ext4_write_begin,
+	.write_end	= ext4_ordered_write_end,
 	.bmap		= ext4_bmap,
 	.invalidatepage	= ext4_invalidatepage,
 	.releasepage	= ext4_releasepage,
@@ -1719,8 +1758,8 @@ static const struct address_space_operat
 	.readpages	= ext4_readpages,
 	.writepage	= ext4_writeback_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext4_prepare_write,
-	.commit_write	= ext4_writeback_commit_write,
+	.write_begin	= ext4_write_begin,
+	.write_end	= ext4_writeback_write_end,
 	.bmap		= ext4_bmap,
 	.invalidatepage	= ext4_invalidatepage,
 	.releasepage	= ext4_releasepage,
@@ -1733,8 +1772,8 @@ static const struct address_space_operat
 	.readpages	= ext4_readpages,
 	.writepage	= ext4_journalled_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= ext4_prepare_write,
-	.commit_write	= ext4_journalled_commit_write,
+	.write_begin	= ext4_write_begin,
+	.write_end	= ext4_journalled_write_end,
 	.set_page_dirty	= ext4_journalled_set_page_dirty,
 	.bmap		= ext4_bmap,
 	.invalidatepage	= ext4_invalidatepage,
#27 lore raw

PATCH 19/41 xfs convert to new aops.

In reply to [email protected]
 fs/xfs/linux-2.6/xfs_aops.c |   19 ++++++++++++-------
 fs/xfs/linux-2.6/xfs_lrw.c  |   35 ++++++++++++-----------------------
 2 files changed, 24 insertions(+), 30 deletions(-)
fs/xfs/linux-2.6/xfs_aops.c +12 -7
Index: linux-2.6/fs/xfs/linux-2.6/xfs_aops.c
===================================================================
@@ -1479,13 +1479,18 @@ xfs_vm_direct_IO(
 }
 
 STATIC int
-xfs_vm_prepare_write(
+xfs_vm_write_begin(
 	struct file		*file,
-	struct page		*page,
-	unsigned int		from,
-	unsigned int		to)
+	struct address_space	*mapping,
+	loff_t			pos,
+	unsigned		len,
+	unsigned		flags,
+	struct page		**pagep,
+	void			**fsdata)
 {
-	return block_prepare_write(page, from, to, xfs_get_blocks);
+	*pagep = NULL;
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+								xfs_get_blocks);
 }
 
 STATIC sector_t
@@ -1539,8 +1544,8 @@ const struct address_space_operations xf
 	.sync_page		= block_sync_page,
 	.releasepage		= xfs_vm_releasepage,
 	.invalidatepage		= xfs_vm_invalidatepage,
-	.prepare_write		= xfs_vm_prepare_write,
-	.commit_write		= generic_commit_write,
+	.write_begin		= xfs_vm_write_begin,
+	.write_end		= generic_write_end,
 	.bmap			= xfs_vm_bmap,
 	.direct_IO		= xfs_vm_direct_IO,
 	.migratepage		= buffer_migrate_page,
fs/xfs/linux-2.6/xfs_lrw.c +12 -23
Index: linux-2.6/fs/xfs/linux-2.6/xfs_lrw.c
===================================================================
@@ -134,45 +134,34 @@ xfs_iozero(
 	loff_t			pos,	/* offset in file		*/
 	size_t			count)	/* size of data to zero		*/
 {
-	unsigned		bytes;
 	struct page		*page;
 	struct address_space	*mapping;
 	int			status;
 
 	mapping = ip->i_mapping;
 	do {
-		unsigned long index, offset;
+		unsigned offset, bytes;
+		void *fsdata;
 
 		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
-		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
 		if (bytes > count)
 			bytes = count;
 
-		status = -ENOMEM;
-		page = grab_cache_page(mapping, index);
-		if (!page)
-			break;
-
-		status = mapping->a_ops->prepare_write(NULL, page, offset,
-							offset + bytes);
+		status = pagecache_write_begin(NULL, mapping, pos, bytes,
+					AOP_FLAG_UNINTERRUPTIBLE,
+					&page, &fsdata);
 		if (status)
-			goto unlock;
+			break;
 
 		zero_user_page(page, offset, bytes, KM_USER0);
 
-		status = mapping->a_ops->commit_write(NULL, page, offset,
-							offset + bytes);
-		if (!status) {
-			pos += bytes;
-			count -= bytes;
-		}
-
-unlock:
-		unlock_page(page);
-		page_cache_release(page);
-		if (status)
-			break;
+		status = pagecache_write_end(NULL, mapping, pos, bytes, bytes,
+					page, fsdata);
+		WARN_ON(status <= 0); /* can't return less than zero! */
+		pos += bytes;
+		count -= bytes;
+		status = 0;
 	} while (count);
 
 	return (-status);
#28 lore raw

PATCH 20/41 fs: new cont helpers

In reply to [email protected]

Rework the generic block "cont" routines to handle the new aops. Supporting cont_prepare_write would take quite a lot of code to support, so remove it instead (and we later convert all filesystems to use it).

write_begin gets passed AOP_FLAG_CONT_EXPAND when called from generic_cont_expand, so filesystems can avoid the old hacks they used.

 fs/buffer.c                 |  198 +++++++++++++++++++++-----------------------
 include/linux/buffer_head.h |    5 -
 include/linux/fs.h          |    1 
 mm/filemap.c                |    5 +
 4 files changed, 105 insertions(+), 104 deletions(-)
fs/buffer.c +96 -102
Index: linux-2.6/fs/buffer.c
===================================================================
@@ -2156,14 +2156,14 @@ int block_read_full_page(struct page *pa
 }
 
 /* utility function for filesystems that need to do work on expanding
- * truncates.  Uses prepare/commit_write to allow the filesystem to
+ * truncates.  Uses filesystem pagecache writes to allow the filesystem to
  * deal with the hole.  
  */
-static int __generic_cont_expand(struct inode *inode, loff_t size,
-				 pgoff_t index, unsigned int offset)
+int generic_cont_expand_simple(struct inode *inode, loff_t size)
 {
 	struct address_space *mapping = inode->i_mapping;
 	struct page *page;
+	void *fsdata;
 	unsigned long limit;
 	int err;
 
@@ -2176,140 +2176,134 @@ static int __generic_cont_expand(struct 
 	if (size > inode->i_sb->s_maxbytes)
 		goto out;
 
-	err = -ENOMEM;
-	page = grab_cache_page(mapping, index);
-	if (!page)
-		goto out;
-	err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
-	if (err) {
-		/*
-		 * ->prepare_write() may have instantiated a few blocks
-		 * outside i_size.  Trim these off again.
-		 */
-		unlock_page(page);
-		page_cache_release(page);
-		vmtruncate(inode, inode->i_size);
+	err = pagecache_write_begin(NULL, mapping, size, 0,
+				AOP_FLAG_UNINTERRUPTIBLE|AOP_FLAG_CONT_EXPAND,
+				&page, &fsdata);
+	if (err)
 		goto out;
-	}
 
-	err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+	err = pagecache_write_end(NULL, mapping, size, 0, 0, page, fsdata);
+	BUG_ON(err > 0);
 
-	unlock_page(page);
-	page_cache_release(page);
-	if (err > 0)
-		err = 0;
 out:
 	return err;
 }
 
 int generic_cont_expand(struct inode *inode, loff_t size)
 {
-	pgoff_t index;
 	unsigned int offset;
 
 	offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
 
 	/* ugh.  in prepare/commit_write, if from==to==start of block, we
-	** skip the prepare.  make sure we never send an offset for the start
-	** of a block
-	*/
+	 * skip the prepare.  make sure we never send an offset for the start
+	 * of a block.
+	 * XXX: actually, this should be handled in those filesystems by
+	 * checking for the AOP_FLAG_CONT_EXPAND flag.
+	 */
 	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
 		/* caller must handle this extra byte. */
-		offset++;
+		size++;
 	}
-	index = size >> PAGE_CACHE_SHIFT;
-
-	return __generic_cont_expand(inode, size, index, offset);
-}
-
-int generic_cont_expand_simple(struct inode *inode, loff_t size)
-{
-	loff_t pos = size - 1;
-	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
-	unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
-
-	/* prepare/commit_write can handle even if from==to==start of block. */
-	return __generic_cont_expand(inode, size, index, offset);
+	return generic_cont_expand_simple(inode, size);
 }
 
-/*
- * For moronic filesystems that do not allow holes in file.
- * We may have to extend the file.
- */
-
-int cont_prepare_write(struct page *page, unsigned offset,
-		unsigned to, get_block_t *get_block, loff_t *bytes)
+int cont_expand_zero(struct file *file, struct address_space *mapping,
+			loff_t pos, loff_t *bytes)
 {
-	struct address_space *mapping = page->mapping;
 	struct inode *inode = mapping->host;
-	struct page *new_page;
-	pgoff_t pgpos;
-	long status;
-	unsigned zerofrom;
 	unsigned blocksize = 1 << inode->i_blkbits;
+	struct page *page;
+	void *fsdata;
+	pgoff_t index, curidx;
+	loff_t curpos;
+	unsigned zerofrom, offset, len;
+	int err = 0;
 
-	while(page->index > (pgpos = *bytes>>PAGE_CACHE_SHIFT)) {
-		status = -ENOMEM;
-		new_page = grab_cache_page(mapping, pgpos);
-		if (!new_page)
-			goto out;
-		/* we might sleep */
-		if (*bytes>>PAGE_CACHE_SHIFT != pgpos) {
-			unlock_page(new_page);
-			page_cache_release(new_page);
-			continue;
-		}
-		zerofrom = *bytes & ~PAGE_CACHE_MASK;
+	index = pos >> PAGE_CACHE_SHIFT;
+	offset = pos & ~PAGE_CACHE_MASK;
+
+	while (index > (curidx = (curpos = *bytes)>>PAGE_CACHE_SHIFT)) {
+		zerofrom = curpos & ~PAGE_CACHE_MASK;
 		if (zerofrom & (blocksize-1)) {
 			*bytes |= (blocksize-1);
 			(*bytes)++;
 		}
-		status = __block_prepare_write(inode, new_page, zerofrom,
-						PAGE_CACHE_SIZE, get_block);
-		if (status)
-			goto out_unmap;
-		zero_user_page(new_page, zerofrom, PAGE_CACHE_SIZE - zerofrom,
-				KM_USER0);
-		generic_commit_write(NULL, new_page, zerofrom, PAGE_CACHE_SIZE);
-		unlock_page(new_page);
-		page_cache_release(new_page);
-	}
+		len = PAGE_CACHE_SIZE - zerofrom;
 
-	if (page->index < pgpos) {
-		/* completely inside the area */
-		zerofrom = offset;
-	} else {
-		/* page covers the boundary, find the boundary offset */
-		zerofrom = *bytes & ~PAGE_CACHE_MASK;
+		err = pagecache_write_begin(file, mapping, curpos, len,
+						AOP_FLAG_UNINTERRUPTIBLE,
+						&page, &fsdata);
+		if (err)
+			goto out;
+		zero_user_page(page, zerofrom, len, KM_USER0);
+		err = pagecache_write_end(file, mapping, curpos, len, len,
+						page, fsdata);
+		if (err < 0)
+			goto out;
+		BUG_ON(err != len);
+		err = 0;
+	}
 
+	/* page covers the boundary, find the boundary offset */
+	if (index == curidx) {
+		zerofrom = curpos & ~PAGE_CACHE_MASK;
 		/* if we will expand the thing last block will be filled */
-		if (to > zerofrom && (zerofrom & (blocksize-1))) {
+		if (offset <= zerofrom) {
+			goto out;
+		}
+		if (zerofrom & (blocksize-1)) {
 			*bytes |= (blocksize-1);
 			(*bytes)++;
 		}
+		len = offset - zerofrom;
 
-		/* starting below the boundary? Nothing to zero out */
-		if (offset <= zerofrom)
-			zerofrom = offset;
-	}
-	status = __block_prepare_write(inode, page, zerofrom, to, get_block);
-	if (status)
-		goto out1;
-	if (zerofrom < offset) {
-		zero_user_page(page, zerofrom, offset - zerofrom, KM_USER0);
-		__block_commit_write(inode, page, zerofrom, offset);
+		err = pagecache_write_begin(file, mapping, curpos, len,
+						AOP_FLAG_UNINTERRUPTIBLE,
+						&page, &fsdata);
+		if (err)
+			goto out;
+		zero_user_page(page, zerofrom, len, KM_USER0);
+		err = pagecache_write_end(file, mapping, curpos, len, len,
+						page, fsdata);
+		if (err < 0)
+			goto out;
+		BUG_ON(err != len);
+		err = 0;
 	}
-	return 0;
-out1:
-	ClearPageUptodate(page);
-	return status;
-
-out_unmap:
-	ClearPageUptodate(new_page);
-	unlock_page(new_page);
-	page_cache_release(new_page);
 out:
-	return status;
+	return err;
+}
+
+/*
+ * For moronic filesystems that do not allow holes in file.
+ * We may have to extend the file.
+ */
+int cont_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata,
+			get_block_t *get_block, loff_t *bytes)
+{
+	struct inode *inode = mapping->host;
+	unsigned blocksize = 1 << inode->i_blkbits;
+	unsigned zerofrom;
+	int err;
+
+	err = cont_expand_zero(file, mapping, pos, bytes);
+	if (err)
+		goto out;
+
+	zerofrom = *bytes & ~PAGE_CACHE_MASK;
+	if (pos+len > *bytes && zerofrom & (blocksize-1)) {
+		*bytes |= (blocksize-1);
+		(*bytes)++;
+	}
+
+	*pagep = NULL;
+	err = block_write_begin(file, mapping, pos, len,
+				flags, pagep, fsdata, get_block);
+out:
+	return err;
 }
 
 int block_prepare_write(struct page *page, unsigned from, unsigned to,
@@ -3134,7 +3128,7 @@ EXPORT_SYMBOL(block_read_full_page);
 EXPORT_SYMBOL(block_sync_page);
 EXPORT_SYMBOL(block_truncate_page);
 EXPORT_SYMBOL(block_write_full_page);
-EXPORT_SYMBOL(cont_prepare_write);
+EXPORT_SYMBOL(cont_write_begin);
 EXPORT_SYMBOL(end_buffer_read_sync);
 EXPORT_SYMBOL(end_buffer_write_sync);
 EXPORT_SYMBOL(file_fsync);
include/linux/buffer_head.h +3 -2
Index: linux-2.6/include/linux/buffer_head.h
===================================================================
@@ -214,8 +214,9 @@ int generic_write_end(struct file *, str
 				struct page *, void *);
 void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
 int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
-int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
-				loff_t *);
+int cont_write_begin(struct file *, struct address_space *, loff_t,
+			unsigned, unsigned, struct page **, void **,
+			get_block_t *, loff_t *);
 int generic_cont_expand(struct inode *inode, loff_t size);
 int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
include/linux/fs.h +1 -0
Index: linux-2.6/include/linux/fs.h
===================================================================
@@ -410,6 +410,7 @@ enum positive_aop_returns {
 };
 
 #define AOP_FLAG_UNINTERRUPTIBLE	0x0001 /* will not do a short write */
+#define AOP_FLAG_CONT_EXPAND		0x0002 /* called from cont_expand */
 
 /*
  * oh the beauties of C type declarations.
mm/filemap.c +5 -0
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -1756,6 +1756,7 @@ size_t iov_iter_copy_from_user_atomic(st
 
 	return copied;
 }
+EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
 
 /*
  * This has the same sideeffects and return value as
@@ -1782,6 +1783,7 @@ size_t iov_iter_copy_from_user(struct pa
 	kunmap(page);
 	return copied;
 }
+EXPORT_SYMBOL(iov_iter_copy_from_user);
 
 static void __iov_iter_advance_iov(struct iov_iter *i, size_t bytes)
 {
@@ -1813,12 +1815,14 @@ void iov_iter_advance(struct iov_iter *i
 	__iov_iter_advance_iov(i, bytes);
 	i->count -= bytes;
 }
+EXPORT_SYMBOL(iov_iter_advance);
 
 int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
 {
 	char __user *buf = i->iov->iov_base + i->iov_offset;
 	return fault_in_pages_readable(buf, bytes);
 }
+EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
 /*
  * Return the count of just the current iov_iter segment.
@@ -1831,6 +1835,7 @@ size_t iov_iter_single_seg_count(struct 
 	else
 		return min(i->count, iov->iov_len - i->iov_offset);
 }
+EXPORT_SYMBOL(iov_iter_single_seg_count);
 
 /*
  * Performs necessary checks before doing a write
#29 lore raw

PATCH 21/41 fat convert to new aops.

In reply to [email protected]
 fs/fat/inode.c |   27 ++++++++++++++++-----------
 1 file changed, 16 insertions(+), 11 deletions(-)
fs/fat/inode.c +16 -11
Index: linux-2.6/fs/fat/inode.c
===================================================================
@@ -141,19 +141,24 @@ static int fat_readpages(struct file *fi
 	return mpage_readpages(mapping, pages, nr_pages, fat_get_block);
 }
 
-static int fat_prepare_write(struct file *file, struct page *page,
-			     unsigned from, unsigned to)
+static int fat_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return cont_prepare_write(page, from, to, fat_get_block,
-				  &MSDOS_I(page->mapping->host)->mmu_private);
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				fat_get_block,
+				&MSDOS_I(mapping->host)->mmu_private);
 }
 
-static int fat_commit_write(struct file *file, struct page *page,
-			    unsigned from, unsigned to)
+static int fat_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *pagep, void *fsdata)
 {
-	struct inode *inode = page->mapping->host;
-	int err = generic_commit_write(file, page, from, to);
-	if (!err && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
+	struct inode *inode = mapping->host;
+	int err;
+	err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata);
+	if (!(err < 0) && !(MSDOS_I(inode)->i_attrs & ATTR_ARCH)) {
 		inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
 		MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
 		mark_inode_dirty(inode);
@@ -202,8 +207,8 @@ static const struct address_space_operat
 	.writepage	= fat_writepage,
 	.writepages	= fat_writepages,
 	.sync_page	= block_sync_page,
-	.prepare_write	= fat_prepare_write,
-	.commit_write	= fat_commit_write,
+	.write_begin	= fat_write_begin,
+	.write_end	= fat_write_end,
 	.direct_IO	= fat_direct_IO,
 	.bmap		= _fat_bmap
 };
#30 lore raw

PATCH 22/41 adfs convert to new aops.

In reply to [email protected]
 fs/adfs/inode.c |   14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)
fs/adfs/inode.c +9 -5
Index: linux-2.6/fs/adfs/inode.c
===================================================================
@@ -61,10 +61,14 @@ static int adfs_readpage(struct file *fi
 	return block_read_full_page(page, adfs_get_block);
 }
 
-static int adfs_prepare_write(struct file *file, struct page *page, unsigned int from, unsigned int to)
+static int adfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return cont_prepare_write(page, from, to, adfs_get_block,
-		&ADFS_I(page->mapping->host)->mmu_private);
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				adfs_get_block,
+				&ADFS_I(mapping->host)->mmu_private);
 }
 
 static sector_t _adfs_bmap(struct address_space *mapping, sector_t block)
@@ -76,8 +80,8 @@ static const struct address_space_operat
 	.readpage	= adfs_readpage,
 	.writepage	= adfs_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= adfs_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= adfs_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= _adfs_bmap
 };
 
#32 lore raw

Re: [patch 22/41] adfs convert to new aops.

Quoted 6 lines · Russell King
Quoted 3 lines · On Fri, May 25, 2007 at 10:22:06PM +1000, [email protected] wrote:

Applied, thanks.

Note that it won't apply upstream yet until the new aops infrastrcture gets merged.

I'll continue to try to keep the patchset together so nothing breaks... you shouldn't have to do anything, but an ack or nack would be nice when you get time.

Thanks,
Nick

#33 lore raw

PATCH 23/41 hfs convert to new aops.

In reply to [email protected]
 fs/hfs/extent.c |   19 ++++++++-----------
 fs/hfs/inode.c  |   20 ++++++++++++--------
 2 files changed, 20 insertions(+), 19 deletions(-)
fs/hfs/inode.c +12 -8
Index: linux-2.6/fs/hfs/inode.c
===================================================================
@@ -35,10 +35,14 @@ static int hfs_readpage(struct file *fil
 	return block_read_full_page(page, hfs_get_block);
 }
 
-static int hfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
-{
-	return cont_prepare_write(page, from, to, hfs_get_block,
-				  &HFS_I(page->mapping->host)->phys_size);
+static int hfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				hfs_get_block,
+				&HFS_I(mapping->host)->phys_size);
 }
 
 static sector_t hfs_bmap(struct address_space *mapping, sector_t block)
@@ -119,8 +123,8 @@ const struct address_space_operations hf
 	.readpage	= hfs_readpage,
 	.writepage	= hfs_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= hfs_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= hfs_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= hfs_bmap,
 	.releasepage	= hfs_releasepage,
 };
@@ -129,8 +133,8 @@ const struct address_space_operations hf
 	.readpage	= hfs_readpage,
 	.writepage	= hfs_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= hfs_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= hfs_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= hfs_bmap,
 	.direct_IO	= hfs_direct_IO,
 	.writepages	= hfs_writepages,
fs/hfs/extent.c +8 -11
Index: linux-2.6/fs/hfs/extent.c
===================================================================
@@ -464,23 +464,20 @@ void hfs_file_truncate(struct inode *ino
 	       (long long)HFS_I(inode)->phys_size, inode->i_size);
 	if (inode->i_size > HFS_I(inode)->phys_size) {
 		struct address_space *mapping = inode->i_mapping;
+		void *fsdata;
 		struct page *page;
 		int res;
 
+		/* XXX: Can use generic_cont_expand? */
 		size = inode->i_size - 1;
-		page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
-		if (!page)
-			return;
-		size &= PAGE_CACHE_SIZE - 1;
-		size++;
-		res = mapping->a_ops->prepare_write(NULL, page, size, size);
-		if (!res)
-			res = mapping->a_ops->commit_write(NULL, page, size, size);
+		res = pagecache_write_begin(NULL, mapping, size+1, 0,
+				AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata);
+		if (!res) {
+			res = pagecache_write_end(NULL, mapping, size+1, 0, 0,
+					page, fsdata);
+		}
 		if (res)
 			inode->i_size = HFS_I(inode)->phys_size;
-		unlock_page(page);
-		page_cache_release(page);
-		mark_inode_dirty(inode);
 		return;
 	} else if (inode->i_size == HFS_I(inode)->phys_size)
 		return;
#34 lore raw

PATCH 24/41 hfsplus convert to new aops.

In reply to [email protected]
 fs/hfsplus/extents.c |   21 +++++++++------------
 fs/hfsplus/inode.c   |   20 ++++++++++++--------
 2 files changed, 21 insertions(+), 20 deletions(-)
fs/hfsplus/inode.c +12 -8
Index: linux-2.6/fs/hfsplus/inode.c
===================================================================
@@ -27,10 +27,14 @@ static int hfsplus_writepage(struct page
 	return block_write_full_page(page, hfsplus_get_block, wbc);
 }
 
-static int hfsplus_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
-{
-	return cont_prepare_write(page, from, to, hfsplus_get_block,
-		&HFSPLUS_I(page->mapping->host).phys_size);
+static int hfsplus_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				hfsplus_get_block,
+				&HFSPLUS_I(mapping->host).phys_size);
 }
 
 static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
@@ -114,8 +118,8 @@ const struct address_space_operations hf
 	.readpage	= hfsplus_readpage,
 	.writepage	= hfsplus_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= hfsplus_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= hfsplus_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= hfsplus_bmap,
 	.releasepage	= hfsplus_releasepage,
 };
@@ -124,8 +128,8 @@ const struct address_space_operations hf
 	.readpage	= hfsplus_readpage,
 	.writepage	= hfsplus_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= hfsplus_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= hfsplus_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= hfsplus_bmap,
 	.direct_IO	= hfsplus_direct_IO,
 	.writepages	= hfsplus_writepages,
fs/hfsplus/extents.c +9 -12
Index: linux-2.6/fs/hfsplus/extents.c
===================================================================
@@ -443,21 +443,18 @@ void hfsplus_file_truncate(struct inode 
 	if (inode->i_size > HFSPLUS_I(inode).phys_size) {
 		struct address_space *mapping = inode->i_mapping;
 		struct page *page;
-		u32 size = inode->i_size - 1;
+		void *fsdata;
+		u32 size = inode->i_size;
 		int res;
 
-		page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
-		if (!page)
-			return;
-		size &= PAGE_CACHE_SIZE - 1;
-		size++;
-		res = mapping->a_ops->prepare_write(NULL, page, size, size);
-		if (!res)
-			res = mapping->a_ops->commit_write(NULL, page, size, size);
+		res = pagecache_write_begin(NULL, mapping, size, 0,
+						AOP_FLAG_UNINTERRUPTIBLE,
+						&page, &fsdata);
 		if (res)
-			inode->i_size = HFSPLUS_I(inode).phys_size;
-		unlock_page(page);
-		page_cache_release(page);
+			return;
+		res = pagecache_write_end(NULL, mapping, size, 0, 0, page, fsdata);
+		if (res < 0)
+			return;
 		mark_inode_dirty(inode);
 		return;
 	} else if (inode->i_size == HFSPLUS_I(inode).phys_size)
#35 lore raw

PATCH 25/41 hpfs convert to new aops.

In reply to [email protected]
 fs/hpfs/file.c |   20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)
fs/hpfs/file.c +14 -6
Index: linux-2.6/fs/hpfs/file.c
===================================================================
@@ -86,25 +86,33 @@ static int hpfs_writepage(struct page *p
 {
 	return block_write_full_page(page,hpfs_get_block, wbc);
 }
+
 static int hpfs_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,hpfs_get_block);
 }
-static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
-{
-	return cont_prepare_write(page,from,to,hpfs_get_block,
-		&hpfs_i(page->mapping->host)->mmu_private);
+
+static int hpfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				hpfs_get_block,
+				&hpfs_i(mapping->host)->mmu_private);
 }
+
 static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block)
 {
 	return generic_block_bmap(mapping,block,hpfs_get_block);
 }
+
 const struct address_space_operations hpfs_aops = {
 	.readpage = hpfs_readpage,
 	.writepage = hpfs_writepage,
 	.sync_page = block_sync_page,
-	.prepare_write = hpfs_prepare_write,
-	.commit_write = generic_commit_write,
+	.write_begin = hpfs_write_begin,
+	.write_end = generic_write_end,
 	.bmap = _hpfs_bmap
 };
 
#36 lore raw

PATCH 26/41 bfs convert to new aops.

In reply to [email protected]
 fs/bfs/file.c |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)
fs/bfs/file.c +8 -4
Index: linux-2.6/fs/bfs/file.c
===================================================================
@@ -145,9 +145,13 @@ static int bfs_readpage(struct file *fil
 	return block_read_full_page(page, bfs_get_block);
 }
 
-static int bfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+static int bfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page, from, to, bfs_get_block);
+	*pagep = NULL;
+	return block_write_begin(file, mapping, pos, len, flags,
+					pagep, fsdata, bfs_get_block);
 }
 
 static sector_t bfs_bmap(struct address_space *mapping, sector_t block)
@@ -159,8 +163,8 @@ const struct address_space_operations bf
 	.readpage	= bfs_readpage,
 	.writepage	= bfs_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= bfs_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= bfs_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= bfs_bmap,
 };
 
#37 lore raw

PATCH 27/41 qnx4 convert to new aops.

In reply to [email protected]
 fs/qnx4/inode.c |   21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)
fs/qnx4/inode.c +13 -8
Index: linux-2.6/fs/qnx4/inode.c
===================================================================
@@ -433,16 +433,21 @@ static int qnx4_writepage(struct page *p
 {
 	return block_write_full_page(page,qnx4_get_block, wbc);
 }
+
 static int qnx4_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,qnx4_get_block);
 }
-static int qnx4_prepare_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
-{
-	struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host);
-	return cont_prepare_write(page, from, to, qnx4_get_block,
-				  &qnx4_inode->mmu_private);
+
+static int qnx4_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	struct qnx4_inode_info *qnx4_inode = qnx4_i(mapping->host);
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				qnx4_get_block,
+				&qnx4_inode->mmu_private);
 }
 static sector_t qnx4_bmap(struct address_space *mapping, sector_t block)
 {
@@ -452,8 +457,8 @@ static const struct address_space_operat
 	.readpage	= qnx4_readpage,
 	.writepage	= qnx4_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= qnx4_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= qnx4_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= qnx4_bmap
 };
 
maintainer Acked-by on v1 27/41
#38 lore raw

Re: [patch 27/41] qnx4 convert to new aops.

Quoted 1 line · On 2007-05-25 14:22:11, [email protected] wrote:

(although we might just as well do away with the 'write' methods completely,
since write-support is "&& BROKEN" anyway)

Cheers
Anders

Quoted 49 lines
 fs/qnx4/inode.c |   21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

Index: linux-2.6/fs/qnx4/inode.c
===================================================================

fs/qnx4/inode.c +13 -8
@@ -433,16 +433,21 @@ static int qnx4_writepage(struct page *p
 {
 	return block_write_full_page(page,qnx4_get_block, wbc);
 }
+
 static int qnx4_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,qnx4_get_block);
 }
-static int qnx4_prepare_write(struct file *file, struct page *page,
-			      unsigned from, unsigned to)
-{
-	struct qnx4_inode_info *qnx4_inode = qnx4_i(page->mapping->host);
-	return cont_prepare_write(page, from, to, qnx4_get_block,
-				  &qnx4_inode->mmu_private);
+
+static int qnx4_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	struct qnx4_inode_info *qnx4_inode = qnx4_i(mapping->host);
+	*pagep = NULL;
+	return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				qnx4_get_block,
+				&qnx4_inode->mmu_private);
 }
 static sector_t qnx4_bmap(struct address_space *mapping, sector_t block)
 {
@@ -452,8 +457,8 @@ static const struct address_space_operat
 	.readpage	= qnx4_readpage,
 	.writepage	= qnx4_writepage,
 	.sync_page	= block_sync_page,
-	.prepare_write	= qnx4_prepare_write,
-	.commit_write	= generic_commit_write,
+	.write_begin	= qnx4_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= qnx4_bmap
 };
 
Signature · 1 line
-- 
#39 lore raw

Re: [patch 27/41] qnx4 convert to new aops.

Quoted 7 lines · Anders Larsen
Quoted 1 line · On 2007-05-25 14:22:11, [email protected] wrote:

(although we might just as well do away with the 'write' methods completely,
since write-support is "&& BROKEN" anyway)

Thanks, I'll put your ack in... I guess it is not the place of these patches to rip out the write methods, but if you do it before this goes in, then I'll cope ;)

Thanks,
Nick

#40 lore raw

PATCH 28/41 reiserfs use generic write.

In reply to [email protected]

From: Vladimir Saveliev <[email protected]>

Make reiserfs to write via generic routines.
Original reiserfs write optimized for big writes is deadlock rone

---

---
 fs/reiserfs/file.c | 1240 -----------------------------------------------------
 1 file changed, 1 insertion(+), 1239 deletions(-)
fs/reiserfs/file.c +1 -1239
Index: linux-2.6/fs/reiserfs/file.c
===================================================================
@@ -153,608 +153,6 @@ static int reiserfs_sync_file(struct fil
 	return (n_err < 0) ? -EIO : 0;
 }
 
-/* I really do not want to play with memory shortage right now, so
-   to simplify the code, we are not going to write more than this much pages at
-   a time. This still should considerably improve performance compared to 4k
-   at a time case. This is 32 pages of 4k size. */
-#define REISERFS_WRITE_PAGES_AT_A_TIME (128 * 1024) / PAGE_CACHE_SIZE
-
-/* Allocates blocks for a file to fulfil write request.
-   Maps all unmapped but prepared pages from the list.
-   Updates metadata with newly allocated blocknumbers as needed */
-static int reiserfs_allocate_blocks_for_region(struct reiserfs_transaction_handle *th, struct inode *inode,	/* Inode we work with */
-					       loff_t pos,	/* Writing position */
-					       int num_pages,	/* number of pages write going
-								   to touch */
-					       int write_bytes,	/* amount of bytes to write */
-					       struct page **prepared_pages,	/* array of
-										   prepared pages
-										 */
-					       int blocks_to_allocate	/* Amount of blocks we
-									   need to allocate to
-									   fit the data into file
-									 */
-    )
-{
-	struct cpu_key key;	// cpu key of item that we are going to deal with
-	struct item_head *ih;	// pointer to item head that we are going to deal with
-	struct buffer_head *bh;	// Buffer head that contains items that we are going to deal with
-	__le32 *item;		// pointer to item we are going to deal with
-	INITIALIZE_PATH(path);	// path to item, that we are going to deal with.
-	b_blocknr_t *allocated_blocks;	// Pointer to a place where allocated blocknumbers would be stored.
-	reiserfs_blocknr_hint_t hint;	// hint structure for block allocator.
-	size_t res;		// return value of various functions that we call.
-	int curr_block;		// current block used to keep track of unmapped blocks.
-	int i;			// loop counter
-	int itempos;		// position in item
-	unsigned int from = (pos & (PAGE_CACHE_SIZE - 1));	// writing position in
-	// first page
-	unsigned int to = ((pos + write_bytes - 1) & (PAGE_CACHE_SIZE - 1)) + 1;	/* last modified byte offset in last page */
-	__u64 hole_size;	// amount of blocks for a file hole, if it needed to be created.
-	int modifying_this_item = 0;	// Flag for items traversal code to keep track
-	// of the fact that we already prepared
-	// current block for journal
-	int will_prealloc = 0;
-	RFALSE(!blocks_to_allocate,
-	       "green-9004: tried to allocate zero blocks?");
-
-	/* only preallocate if this is a small write */
-	if (REISERFS_I(inode)->i_prealloc_count ||
-	    (!(write_bytes & (inode->i_sb->s_blocksize - 1)) &&
-	     blocks_to_allocate <
-	     REISERFS_SB(inode->i_sb)->s_alloc_options.preallocsize))
-		will_prealloc =
-		    REISERFS_SB(inode->i_sb)->s_alloc_options.preallocsize;
-
-	allocated_blocks = kmalloc((blocks_to_allocate + will_prealloc) *
-				   sizeof(b_blocknr_t), GFP_NOFS);
-	if (!allocated_blocks)
-		return -ENOMEM;
-
-	/* First we compose a key to point at the writing position, we want to do
-	   that outside of any locking region. */
-	make_cpu_key(&key, inode, pos + 1, TYPE_ANY, 3 /*key length */ );
-
-	/* If we came here, it means we absolutely need to open a transaction,
-	   since we need to allocate some blocks */
-	reiserfs_write_lock(inode->i_sb);	// Journaling stuff and we need that.
-	res = journal_begin(th, inode->i_sb, JOURNAL_PER_BALANCE_CNT * 3 + 1 + 2 * REISERFS_QUOTA_TRANS_BLOCKS(inode->i_sb));	// Wish I know if this number enough
-	if (res)
-		goto error_exit;
-	reiserfs_update_inode_transaction(inode);
-
-	/* Look for the in-tree position of our write, need path for block allocator */
-	res = search_for_position_by_key(inode->i_sb, &key, &path);
-	if (res == IO_ERROR) {
-		res = -EIO;
-		goto error_exit;
-	}
-
-	/* Allocate blocks */
-	/* First fill in "hint" structure for block allocator */
-	hint.th = th;		// transaction handle.
-	hint.path = &path;	// Path, so that block allocator can determine packing locality or whatever it needs to determine.
-	hint.inode = inode;	// Inode is needed by block allocator too.
-	hint.search_start = 0;	// We have no hint on where to search free blocks for block allocator.
-	hint.key = key.on_disk_key;	// on disk key of file.
-	hint.block = inode->i_blocks >> (inode->i_sb->s_blocksize_bits - 9);	// Number of disk blocks this file occupies already.
-	hint.formatted_node = 0;	// We are allocating blocks for unformatted node.
-	hint.preallocate = will_prealloc;
-
-	/* Call block allocator to allocate blocks */
-	res =
-	    reiserfs_allocate_blocknrs(&hint, allocated_blocks,
-				       blocks_to_allocate, blocks_to_allocate);
-	if (res != CARRY_ON) {
-		if (res == NO_DISK_SPACE) {
-			/* We flush the transaction in case of no space. This way some
-			   blocks might become free */
-			SB_JOURNAL(inode->i_sb)->j_must_wait = 1;
-			res = restart_transaction(th, inode, &path);
-			if (res)
-				goto error_exit;
-
-			/* We might have scheduled, so search again */
-			res =
-			    search_for_position_by_key(inode->i_sb, &key,
-						       &path);
-			if (res == IO_ERROR) {
-				res = -EIO;
-				goto error_exit;
-			}
-
-			/* update changed info for hint structure. */
-			res =
-			    reiserfs_allocate_blocknrs(&hint, allocated_blocks,
-						       blocks_to_allocate,
-						       blocks_to_allocate);
-			if (res != CARRY_ON) {
-				res = res == QUOTA_EXCEEDED ? -EDQUOT : -ENOSPC;
-				pathrelse(&path);
-				goto error_exit;
-			}
-		} else {
-			res = res == QUOTA_EXCEEDED ? -EDQUOT : -ENOSPC;
-			pathrelse(&path);
-			goto error_exit;
-		}
-	}
-#ifdef __BIG_ENDIAN
-	// Too bad, I have not found any way to convert a given region from
-	// cpu format to little endian format
-	{
-		int i;
-		for (i = 0; i < blocks_to_allocate; i++)
-			allocated_blocks[i] = cpu_to_le32(allocated_blocks[i]);
-	}
-#endif
-
-	/* Blocks allocating well might have scheduled and tree might have changed,
-	   let's search the tree again */
-	/* find where in the tree our write should go */
-	res = search_for_position_by_key(inode->i_sb, &key, &path);
-	if (res == IO_ERROR) {
-		res = -EIO;
-		goto error_exit_free_blocks;
-	}
-
-	bh = get_last_bh(&path);	// Get a bufferhead for last element in path.
-	ih = get_ih(&path);	// Get a pointer to last item head in path.
-	item = get_item(&path);	// Get a pointer to last item in path
-
-	/* Let's see what we have found */
-	if (res != POSITION_FOUND) {	/* position not found, this means that we
-					   might need to append file with holes
-					   first */
-		// Since we are writing past the file's end, we need to find out if
-		// there is a hole that needs to be inserted before our writing
-		// position, and how many blocks it is going to cover (we need to
-		//  populate pointers to file blocks representing the hole with zeros)
-
-		{
-			int item_offset = 1;
-			/*
-			 * if ih is stat data, its offset is 0 and we don't want to
-			 * add 1 to pos in the hole_size calculation
-			 */
-			if (is_statdata_le_ih(ih))
-				item_offset = 0;
-			hole_size = (pos + item_offset -
-				     (le_key_k_offset
-				      (get_inode_item_key_version(inode),
-				       &(ih->ih_key)) + op_bytes_number(ih,
-									inode->
-									i_sb->
-									s_blocksize)))
-			    >> inode->i_sb->s_blocksize_bits;
-		}
-
-		if (hole_size > 0) {
-			int to_paste = min_t(__u64, hole_size, MAX_ITEM_LEN(inode->i_sb->s_blocksize) / UNFM_P_SIZE);	// How much data to insert first time.
-			/* area filled with zeroes, to supply as list of zero blocknumbers
-			   We allocate it outside of loop just in case loop would spin for
-			   several iterations. */
-			char *zeros = kzalloc(to_paste * UNFM_P_SIZE, GFP_ATOMIC);	// We cannot insert more than MAX_ITEM_LEN bytes anyway.
-			if (!zeros) {
-				res = -ENOMEM;
-				goto error_exit_free_blocks;
-			}
-			do {
-				to_paste =
-				    min_t(__u64, hole_size,
-					  MAX_ITEM_LEN(inode->i_sb->
-						       s_blocksize) /
-					  UNFM_P_SIZE);
-				if (is_indirect_le_ih(ih)) {
-					/* Ok, there is existing indirect item already. Need to append it */
-					/* Calculate position past inserted item */
-					make_cpu_key(&key, inode,
-						     le_key_k_offset
-						     (get_inode_item_key_version
-						      (inode),
-						      &(ih->ih_key)) +
-						     op_bytes_number(ih,
-								     inode->
-								     i_sb->
-								     s_blocksize),
-						     TYPE_INDIRECT, 3);
-					res =
-					    reiserfs_paste_into_item(th, &path,
-								     &key,
-								     inode,
-								     (char *)
-								     zeros,
-								     UNFM_P_SIZE
-								     *
-								     to_paste);
-					if (res) {
-						kfree(zeros);
-						goto error_exit_free_blocks;
-					}
-				} else if (is_statdata_le_ih(ih)) {
-					/* No existing item, create it */
-					/* item head for new item */
-					struct item_head ins_ih;
-
-					/* create a key for our new item */
-					make_cpu_key(&key, inode, 1,
-						     TYPE_INDIRECT, 3);
-
-					/* Create new item head for our new item */
-					make_le_item_head(&ins_ih, &key,
-							  key.version, 1,
-							  TYPE_INDIRECT,
-							  to_paste *
-							  UNFM_P_SIZE,
-							  0 /* free space */ );
-
-					/* Find where such item should live in the tree */
-					res =
-					    search_item(inode->i_sb, &key,
-							&path);
-					if (res != ITEM_NOT_FOUND) {
-						/* item should not exist, otherwise we have error */
-						if (res != -ENOSPC) {
-							reiserfs_warning(inode->
-									 i_sb,
-									 "green-9008: search_by_key (%K) returned %d",
-									 &key,
-									 res);
-						}
-						res = -EIO;
-						kfree(zeros);
-						goto error_exit_free_blocks;
-					}
-					res =
-					    reiserfs_insert_item(th, &path,
-								 &key, &ins_ih,
-								 inode,
-								 (char *)zeros);
-				} else {
-					reiserfs_panic(inode->i_sb,
-						       "green-9011: Unexpected key type %K\n",
-						       &key);
-				}
-				if (res) {
-					kfree(zeros);
-					goto error_exit_free_blocks;
-				}
-				/* Now we want to check if transaction is too full, and if it is
-				   we restart it. This will also free the path. */
-				if (journal_transaction_should_end
-				    (th, th->t_blocks_allocated)) {
-					inode->i_size = cpu_key_k_offset(&key) +
-						(to_paste << inode->i_blkbits);
-					res =
-					    restart_transaction(th, inode,
-								&path);
-					if (res) {
-						pathrelse(&path);
-						kfree(zeros);
-						goto error_exit;
-					}
-				}
-
-				/* Well, need to recalculate path and stuff */
-				set_cpu_key_k_offset(&key,
-						     cpu_key_k_offset(&key) +
-						     (to_paste << inode->
-						      i_blkbits));
-				res =
-				    search_for_position_by_key(inode->i_sb,
-							       &key, &path);
-				if (res == IO_ERROR) {
-					res = -EIO;
-					kfree(zeros);
-					goto error_exit_free_blocks;
-				}
-				bh = get_last_bh(&path);
-				ih = get_ih(&path);
-				item = get_item(&path);
-				hole_size -= to_paste;
-			} while (hole_size);
-			kfree(zeros);
-		}
-	}
-	// Go through existing indirect items first
-	// replace all zeroes with blocknumbers from list
-	// Note that if no corresponding item was found, by previous search,
-	// it means there are no existing in-tree representation for file area
-	// we are going to overwrite, so there is nothing to scan through for holes.
-	for (curr_block = 0, itempos = path.pos_in_item;
-	     curr_block < blocks_to_allocate && res == POSITION_FOUND;) {
-	      retry:
-
-		if (itempos >= ih_item_len(ih) / UNFM_P_SIZE) {
-			/* We run out of data in this indirect item, let's look for another
-			   one. */
-			/* First if we are already modifying current item, log it */
-			if (modifying_this_item) {
-				journal_mark_dirty(th, inode->i_sb, bh);
-				modifying_this_item = 0;
-			}
-			/* Then set the key to look for a new indirect item (offset of old
-			   item is added to old item length */
-			set_cpu_key_k_offset(&key,
-					     le_key_k_offset
-					     (get_inode_item_key_version(inode),
-					      &(ih->ih_key)) +
-					     op_bytes_number(ih,
-							     inode->i_sb->
-							     s_blocksize));
-			/* Search ofor position of new key in the tree. */
-			res =
-			    search_for_position_by_key(inode->i_sb, &key,
-						       &path);
-			if (res == IO_ERROR) {
-				res = -EIO;
-				goto error_exit_free_blocks;
-			}
-			bh = get_last_bh(&path);
-			ih = get_ih(&path);
-			item = get_item(&path);
-			itempos = path.pos_in_item;
-			continue;	// loop to check all kinds of conditions and so on.
-		}
-		/* Ok, we have correct position in item now, so let's see if it is
-		   representing file hole (blocknumber is zero) and fill it if needed */
-		if (!item[itempos]) {
-			/* Ok, a hole. Now we need to check if we already prepared this
-			   block to be journaled */
-			while (!modifying_this_item) {	// loop until succeed
-				/* Well, this item is not journaled yet, so we must prepare
-				   it for journal first, before we can change it */
-				struct item_head tmp_ih;	// We copy item head of found item,
-				// here to detect if fs changed under
-				// us while we were preparing for
-				// journal.
-				int fs_gen;	// We store fs generation here to find if someone
-				// changes fs under our feet
-
-				copy_item_head(&tmp_ih, ih);	// Remember itemhead
-				fs_gen = get_generation(inode->i_sb);	// remember fs generation
-				reiserfs_prepare_for_journal(inode->i_sb, bh, 1);	// Prepare a buffer within which indirect item is stored for changing.
-				if (fs_changed(fs_gen, inode->i_sb)
-				    && item_moved(&tmp_ih, &path)) {
-					// Sigh, fs was changed under us, we need to look for new
-					// location of item we are working with
-
-					/* unmark prepaerd area as journaled and search for it's
-					   new position */
-					reiserfs_restore_prepared_buffer(inode->
-									 i_sb,
-									 bh);
-					res =
-					    search_for_position_by_key(inode->
-								       i_sb,
-								       &key,
-								       &path);
-					if (res == IO_ERROR) {
-						res = -EIO;
-						goto error_exit_free_blocks;
-					}
-					bh = get_last_bh(&path);
-					ih = get_ih(&path);
-					item = get_item(&path);
-					itempos = path.pos_in_item;
-					goto retry;
-				}
-				modifying_this_item = 1;
-			}
-			item[itempos] = allocated_blocks[curr_block];	// Assign new block
-			curr_block++;
-		}
-		itempos++;
-	}
-
-	if (modifying_this_item) {	// We need to log last-accessed block, if it
-		// was modified, but not logged yet.
-		journal_mark_dirty(th, inode->i_sb, bh);
-	}
-
-	if (curr_block < blocks_to_allocate) {
-		// Oh, well need to append to indirect item, or to create indirect item
-		// if there weren't any
-		if (is_indirect_le_ih(ih)) {
-			// Existing indirect item - append. First calculate key for append
-			// position. We do not need to recalculate path as it should
-			// already point to correct place.
-			make_cpu_key(&key, inode,
-				     le_key_k_offset(get_inode_item_key_version
-						     (inode),
-						     &(ih->ih_key)) +
-				     op_bytes_number(ih,
-						     inode->i_sb->s_blocksize),
-				     TYPE_INDIRECT, 3);
-			res =
-			    reiserfs_paste_into_item(th, &path, &key, inode,
-						     (char *)(allocated_blocks +
-							      curr_block),
-						     UNFM_P_SIZE *
-						     (blocks_to_allocate -
-						      curr_block));
-			if (res) {
-				goto error_exit_free_blocks;
-			}
-		} else if (is_statdata_le_ih(ih)) {
-			// Last found item was statdata. That means we need to create indirect item.
-			struct item_head ins_ih;	/* itemhead for new item */
-
-			/* create a key for our new item */
-			make_cpu_key(&key, inode, 1, TYPE_INDIRECT, 3);	// Position one,
-			// because that's
-			// where first
-			// indirect item
-			// begins
-			/* Create new item head for our new item */
-			make_le_item_head(&ins_ih, &key, key.version, 1,
-					  TYPE_INDIRECT,
-					  (blocks_to_allocate -
-					   curr_block) * UNFM_P_SIZE,
-					  0 /* free space */ );
-			/* Find where such item should live in the tree */
-			res = search_item(inode->i_sb, &key, &path);
-			if (res != ITEM_NOT_FOUND) {
-				/* Well, if we have found such item already, or some error
-				   occured, we need to warn user and return error */
-				if (res != -ENOSPC) {
-					reiserfs_warning(inode->i_sb,
-							 "green-9009: search_by_key (%K) "
-							 "returned %d", &key,
-							 res);
-				}
-				res = -EIO;
-				goto error_exit_free_blocks;
-			}
-			/* Insert item into the tree with the data as its body */
-			res =
-			    reiserfs_insert_item(th, &path, &key, &ins_ih,
-						 inode,
-						 (char *)(allocated_blocks +
-							  curr_block));
-		} else {
-			reiserfs_panic(inode->i_sb,
-				       "green-9010: unexpected item type for key %K\n",
-				       &key);
-		}
-	}
-	// the caller is responsible for closing the transaction
-	// unless we return an error, they are also responsible for logging
-	// the inode.
-	//
-	pathrelse(&path);
-	/*
-	 * cleanup prellocation from previous writes
-	 * if this is a partial block write
-	 */
-	if (write_bytes & (inode->i_sb->s_blocksize - 1))
-		reiserfs_discard_prealloc(th, inode);
-	reiserfs_write_unlock(inode->i_sb);
-
-	// go through all the pages/buffers and map the buffers to newly allocated
-	// blocks (so that system knows where to write these pages later).
-	curr_block = 0;
-	for (i = 0; i < num_pages; i++) {
-		struct page *page = prepared_pages[i];	//current page
-		struct buffer_head *head = page_buffers(page);	// first buffer for a page
-		int block_start, block_end;	// in-page offsets for buffers.
-
-		if (!page_buffers(page))
-			reiserfs_panic(inode->i_sb,
-				       "green-9005: No buffers for prepared page???");
-
-		/* For each buffer in page */
-		for (bh = head, block_start = 0; bh != head || !block_start;
-		     block_start = block_end, bh = bh->b_this_page) {
-			if (!bh)
-				reiserfs_panic(inode->i_sb,
-					       "green-9006: Allocated but absent buffer for a page?");
-			block_end = block_start + inode->i_sb->s_blocksize;
-			if (i == 0 && block_end <= from)
-				/* if this buffer is before requested data to map, skip it */
-				continue;
-			if (i == num_pages - 1 && block_start >= to)
-				/* If this buffer is after requested data to map, abort
-				   processing of current page */
-				break;
-
-			if (!buffer_mapped(bh)) {	// Ok, unmapped buffer, need to map it
-				map_bh(bh, inode->i_sb,
-				       le32_to_cpu(allocated_blocks
-						   [curr_block]));
-				curr_block++;
-				set_buffer_new(bh);
-			}
-		}
-	}
-
-	RFALSE(curr_block > blocks_to_allocate,
-	       "green-9007: Used too many blocks? weird");
-
-	kfree(allocated_blocks);
-	return 0;
-
-// Need to deal with transaction here.
-      error_exit_free_blocks:
-	pathrelse(&path);
-	// free blocks
-	for (i = 0; i < blocks_to_allocate; i++)
-		reiserfs_free_block(th, inode, le32_to_cpu(allocated_blocks[i]),
-				    1);
-
-      error_exit:
-	if (th->t_trans_id) {
-		int err;
-		// update any changes we made to blk count
-		mark_inode_dirty(inode);
-		err =
-		    journal_end(th, inode->i_sb,
-				JOURNAL_PER_BALANCE_CNT * 3 + 1 +
-				2 * REISERFS_QUOTA_TRANS_BLOCKS(inode->i_sb));
-		if (err)
-			res = err;
-	}
-	reiserfs_write_unlock(inode->i_sb);
-	kfree(allocated_blocks);
-
-	return res;
-}
-
-/* Unlock pages prepared by reiserfs_prepare_file_region_for_write */
-static void reiserfs_unprepare_pages(struct page **prepared_pages,	/* list of locked pages */
-				     size_t num_pages /* amount of pages */ )
-{
-	int i;			// loop counter
-
-	for (i = 0; i < num_pages; i++) {
-		struct page *page = prepared_pages[i];
-
-		try_to_free_buffers(page);
-		unlock_page(page);
-		page_cache_release(page);
-	}
-}
-
-/* This function will copy data from userspace to specified pages within
-   supplied byte range */
-static int reiserfs_copy_from_user_to_file_region(loff_t pos,	/* In-file position */
-						  int num_pages,	/* Number of pages affected */
-						  int write_bytes,	/* Amount of bytes to write */
-						  struct page **prepared_pages,	/* pointer to 
-										   array to
-										   prepared pages
-										 */
-						  const char __user * buf	/* Pointer to user-supplied
-										   data */
-    )
-{
-	long page_fault = 0;	// status of copy_from_user.
-	int i;			// loop counter.
-	int offset;		// offset in page
-
-	for (i = 0, offset = (pos & (PAGE_CACHE_SIZE - 1)); i < num_pages;
-	     i++, offset = 0) {
-		size_t count = min_t(size_t, PAGE_CACHE_SIZE - offset, write_bytes);	// How much of bytes to write to this page
-		struct page *page = prepared_pages[i];	// Current page we process.
-
-		fault_in_pages_readable(buf, count);
-
-		/* Copy data from userspace to the current page */
-		kmap(page);
-		page_fault = __copy_from_user(page_address(page) + offset, buf, count);	// Copy the data.
-		/* Flush processor's dcache for this page */
-		flush_dcache_page(page);
-		kunmap(page);
-		buf += count;
-		write_bytes -= count;
-
-		if (page_fault)
-			break;	// Was there a fault? abort.
-	}
-
-	return page_fault ? -EFAULT : 0;
-}
-
 /* taken fs/buffer.c:__block_commit_write */
 int reiserfs_commit_page(struct inode *inode, struct page *page,
 			 unsigned from, unsigned to)
@@ -824,432 +222,6 @@ int reiserfs_commit_page(struct inode *i
 	return ret;
 }
 
-/* Submit pages for write. This was separated from actual file copying
-   because we might want to allocate block numbers in-between.
-   This function assumes that caller will adjust file size to correct value. */
-static int reiserfs_submit_file_region_for_write(struct reiserfs_transaction_handle *th, struct inode *inode, loff_t pos,	/* Writing position offset */
-						 size_t num_pages,	/* Number of pages to write */
-						 size_t write_bytes,	/* number of bytes to write */
-						 struct page **prepared_pages	/* list of pages */
-    )
-{
-	int status;		// return status of block_commit_write.
-	int retval = 0;		// Return value we are going to return.
-	int i;			// loop counter
-	int offset;		// Writing offset in page.
-	int orig_write_bytes = write_bytes;
-	int sd_update = 0;
-
-	for (i = 0, offset = (pos & (PAGE_CACHE_SIZE - 1)); i < num_pages;
-	     i++, offset = 0) {
-		int count = min_t(int, PAGE_CACHE_SIZE - offset, write_bytes);	// How much of bytes to write to this page
-		struct page *page = prepared_pages[i];	// Current page we process.
-
-		status =
-		    reiserfs_commit_page(inode, page, offset, offset + count);
-		if (status)
-			retval = status;	// To not overcomplicate matters We are going to
-		// submit all the pages even if there was error.
-		// we only remember error status to report it on
-		// exit.
-		write_bytes -= count;
-	}
-	/* now that we've gotten all the ordered buffers marked dirty,
-	 * we can safely update i_size and close any running transaction
-	 */
-	if (pos + orig_write_bytes > inode->i_size) {
-		inode->i_size = pos + orig_write_bytes;	// Set new size
-		/* If the file have grown so much that tail packing is no
-		 * longer possible, reset "need to pack" flag */
-		if ((have_large_tails(inode->i_sb) &&
-		     inode->i_size > i_block_size(inode) * 4) ||
-		    (have_small_tails(inode->i_sb) &&
-		     inode->i_size > i_block_size(inode)))
-			REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask;
-		else if ((have_large_tails(inode->i_sb) &&
-			  inode->i_size < i_block_size(inode) * 4) ||
-			 (have_small_tails(inode->i_sb) &&
-			  inode->i_size < i_block_size(inode)))
-			REISERFS_I(inode)->i_flags |= i_pack_on_close_mask;
-
-		if (th->t_trans_id) {
-			reiserfs_write_lock(inode->i_sb);
-			// this sets the proper flags for O_SYNC to trigger a commit
-			mark_inode_dirty(inode);
-			reiserfs_write_unlock(inode->i_sb);
-		} else {
-			reiserfs_write_lock(inode->i_sb);
-			reiserfs_update_inode_transaction(inode);
-			mark_inode_dirty(inode);
-			reiserfs_write_unlock(inode->i_sb);
-		}
-
-		sd_update = 1;
-	}
-	if (th->t_trans_id) {
-		reiserfs_write_lock(inode->i_sb);
-		if (!sd_update)
-			mark_inode_dirty(inode);
-		status = journal_end(th, th->t_super, th->t_blocks_allocated);
-		if (status)
-			retval = status;
-		reiserfs_write_unlock(inode->i_sb);
-	}
-	th->t_trans_id = 0;
-
-	/* 
-	 * we have to unlock the pages after updating i_size, otherwise
-	 * we race with writepage
-	 */
-	for (i = 0; i < num_pages; i++) {
-		struct page *page = prepared_pages[i];
-		unlock_page(page);
-		mark_page_accessed(page);
-		page_cache_release(page);
-	}
-	return retval;
-}
-
-/* Look if passed writing region is going to touch file's tail
-   (if it is present). And if it is, convert the tail to unformatted node */
-static int reiserfs_check_for_tail_and_convert(struct inode *inode,	/* inode to deal with */
-					       loff_t pos,	/* Writing position */
-					       int write_bytes	/* amount of bytes to write */
-    )
-{
-	INITIALIZE_PATH(path);	// needed for search_for_position
-	struct cpu_key key;	// Key that would represent last touched writing byte.
-	struct item_head *ih;	// item header of found block;
-	int res;		// Return value of various functions we call.
-	int cont_expand_offset;	// We will put offset for generic_cont_expand here
-	// This can be int just because tails are created
-	// only for small files.
-
-/* this embodies a dependency on a particular tail policy */
-	if (inode->i_size >= inode->i_sb->s_blocksize * 4) {
-		/* such a big files do not have tails, so we won't bother ourselves
-		   to look for tails, simply return */
-		return 0;
-	}
-
-	reiserfs_write_lock(inode->i_sb);
-	/* find the item containing the last byte to be written, or if
-	 * writing past the end of the file then the last item of the
-	 * file (and then we check its type). */
-	make_cpu_key(&key, inode, pos + write_bytes + 1, TYPE_ANY,
-		     3 /*key length */ );
-	res = search_for_position_by_key(inode->i_sb, &key, &path);
-	if (res == IO_ERROR) {
-		reiserfs_write_unlock(inode->i_sb);
-		return -EIO;
-	}
-	ih = get_ih(&path);
-	res = 0;
-	if (is_direct_le_ih(ih)) {
-		/* Ok, closest item is file tail (tails are stored in "direct"
-		 * items), so we need to unpack it. */
-		/* To not overcomplicate matters, we just call generic_cont_expand
-		   which will in turn call other stuff and finally will boil down to
-		   reiserfs_get_block() that would do necessary conversion. */
-		cont_expand_offset =
-		    le_key_k_offset(get_inode_item_key_version(inode),
-				    &(ih->ih_key));
-		pathrelse(&path);
-		res = generic_cont_expand(inode, cont_expand_offset);
-	} else
-		pathrelse(&path);
-
-	reiserfs_write_unlock(inode->i_sb);
-	return res;
-}
-
-/* This function locks pages starting from @pos for @inode.
-   @num_pages pages are locked and stored in
-   @prepared_pages array. Also buffers are allocated for these pages.
-   First and last page of the region is read if it is overwritten only
-   partially. If last page did not exist before write (file hole or file
-   append), it is zeroed, then. 
-   Returns number of unallocated blocks that should be allocated to cover
-   new file data.*/
-static int reiserfs_prepare_file_region_for_write(struct inode *inode
-						  /* Inode of the file */ ,
-						  loff_t pos,	/* position in the file */
-						  size_t num_pages,	/* number of pages to
-									   prepare */
-						  size_t write_bytes,	/* Amount of bytes to be
-									   overwritten from
-									   @pos */
-						  struct page **prepared_pages	/* pointer to array
-										   where to store
-										   prepared pages */
-    )
-{
-	int res = 0;		// Return values of different functions we call.
-	unsigned long index = pos >> PAGE_CACHE_SHIFT;	// Offset in file in pages.
-	int from = (pos & (PAGE_CACHE_SIZE - 1));	// Writing offset in first page
-	int to = ((pos + write_bytes - 1) & (PAGE_CACHE_SIZE - 1)) + 1;
-	/* offset of last modified byte in last
-	   page */
-	struct address_space *mapping = inode->i_mapping;	// Pages are mapped here.
-	int i;			// Simple counter
-	int blocks = 0;		/* Return value (blocks that should be allocated) */
-	struct buffer_head *bh, *head;	// Current bufferhead and first bufferhead
-	// of a page.
-	unsigned block_start, block_end;	// Starting and ending offsets of current
-	// buffer in the page.
-	struct buffer_head *wait[2], **wait_bh = wait;	// Buffers for page, if
-	// Page appeared to be not up
-	// to date. Note how we have
-	// at most 2 buffers, this is
-	// because we at most may
-	// partially overwrite two
-	// buffers for one page. One at                                                 // the beginning of write area
-	// and one at the end.
-	// Everything inthe middle gets                                                 // overwritten totally.
-
-	struct cpu_key key;	// cpu key of item that we are going to deal with
-	struct item_head *ih = NULL;	// pointer to item head that we are going to deal with
-	struct buffer_head *itembuf = NULL;	// Buffer head that contains items that we are going to deal with
-	INITIALIZE_PATH(path);	// path to item, that we are going to deal with.
-	__le32 *item = NULL;	// pointer to item we are going to deal with
-	int item_pos = -1;	/* Position in indirect item */
-
-	if (num_pages < 1) {
-		reiserfs_warning(inode->i_sb,
-				 "green-9001: reiserfs_prepare_file_region_for_write "
-				 "called with zero number of pages to process");
-		return -EFAULT;
-	}
-
-	/* We have 2 loops for pages. In first loop we grab and lock the pages, so
-	   that nobody would touch these until we release the pages. Then
-	   we'd start to deal with mapping buffers to blocks. */
-	for (i = 0; i < num_pages; i++) {
-		prepared_pages[i] = grab_cache_page(mapping, index + i);	// locks the page
-		if (!prepared_pages[i]) {
-			res = -ENOMEM;
-			goto failed_page_grabbing;
-		}
-		if (!page_has_buffers(prepared_pages[i]))
-			create_empty_buffers(prepared_pages[i],
-					     inode->i_sb->s_blocksize, 0);
-	}
-
-	/* Let's count amount of blocks for a case where all the blocks
-	   overwritten are new (we will substract already allocated blocks later) */
-	if (num_pages > 2)
-		/* These are full-overwritten pages so we count all the blocks in
-		   these pages are counted as needed to be allocated */
-		blocks =
-		    (num_pages - 2) << (PAGE_CACHE_SHIFT - inode->i_blkbits);
-
-	/* count blocks needed for first page (possibly partially written) */
-	blocks += ((PAGE_CACHE_SIZE - from) >> inode->i_blkbits) + !!(from & (inode->i_sb->s_blocksize - 1));	/* roundup */
-
-	/* Now we account for last page. If last page == first page (we
-	   overwrite only one page), we substract all the blocks past the
-	   last writing position in a page out of already calculated number
-	   of blocks */
-	blocks += ((num_pages > 1) << (PAGE_CACHE_SHIFT - inode->i_blkbits)) -
-	    ((PAGE_CACHE_SIZE - to) >> inode->i_blkbits);
-	/* Note how we do not roundup here since partial blocks still
-	   should be allocated */
-
-	/* Now if all the write area lies past the file end, no point in
-	   maping blocks, since there is none, so we just zero out remaining
-	   parts of first and last pages in write area (if needed) */
-	if ((pos & ~((loff_t) PAGE_CACHE_SIZE - 1)) > inode->i_size) {
-		if (from != 0)		/* First page needs to be partially zeroed */
-			zero_user_page(prepared_pages[0], 0, from, KM_USER0);
-
-		if (to != PAGE_CACHE_SIZE)	/* Last page needs to be partially zeroed */
-			zero_user_page(prepared_pages[num_pages-1], to,
-					PAGE_CACHE_SIZE - to, KM_USER0);
-
-		/* Since all blocks are new - use already calculated value */
-		return blocks;
-	}
-
-	/* Well, since we write somewhere into the middle of a file, there is
-	   possibility we are writing over some already allocated blocks, so
-	   let's map these blocks and substract number of such blocks out of blocks
-	   we need to allocate (calculated above) */
-	/* Mask write position to start on blocksize, we do it out of the
-	   loop for performance reasons */
-	pos &= ~((loff_t) inode->i_sb->s_blocksize - 1);
-	/* Set cpu key to the starting position in a file (on left block boundary) */
-	make_cpu_key(&key, inode,
-		     1 + ((pos) & ~((loff_t) inode->i_sb->s_blocksize - 1)),
-		     TYPE_ANY, 3 /*key length */ );
-
-	reiserfs_write_lock(inode->i_sb);	// We need that for at least search_by_key()
-	for (i = 0; i < num_pages; i++) {
-
-		head = page_buffers(prepared_pages[i]);
-		/* For each buffer in the page */
-		for (bh = head, block_start = 0; bh != head || !block_start;
-		     block_start = block_end, bh = bh->b_this_page) {
-			if (!bh)
-				reiserfs_panic(inode->i_sb,
-					       "green-9002: Allocated but absent buffer for a page?");
-			/* Find where this buffer ends */
-			block_end = block_start + inode->i_sb->s_blocksize;
-			if (i == 0 && block_end <= from)
-				/* if this buffer is before requested data to map, skip it */
-				continue;
-
-			if (i == num_pages - 1 && block_start >= to) {
-				/* If this buffer is after requested data to map, abort
-				   processing of current page */
-				break;
-			}
-
-			if (buffer_mapped(bh) && bh->b_blocknr != 0) {
-				/* This is optimisation for a case where buffer is mapped
-				   and have blocknumber assigned. In case significant amount
-				   of such buffers are present, we may avoid some amount
-				   of search_by_key calls.
-				   Probably it would be possible to move parts of this code
-				   out of BKL, but I afraid that would overcomplicate code
-				   without any noticeable benefit.
-				 */
-				item_pos++;
-				/* Update the key */
-				set_cpu_key_k_offset(&key,
-						     cpu_key_k_offset(&key) +
-						     inode->i_sb->s_blocksize);
-				blocks--;	// Decrease the amount of blocks that need to be
-				// allocated
-				continue;	// Go to the next buffer
-			}
-
-			if (!itembuf ||	/* if first iteration */
-			    item_pos >= ih_item_len(ih) / UNFM_P_SIZE) {	/* or if we progressed past the
-										   current unformatted_item */
-				/* Try to find next item */
-				res =
-				    search_for_position_by_key(inode->i_sb,
-							       &key, &path);
-				/* Abort if no more items */
-				if (res != POSITION_FOUND) {
-					/* make sure later loops don't use this item */
-					itembuf = NULL;
-					item = NULL;
-					break;
-				}
-
-				/* Update information about current indirect item */
-				itembuf = get_last_bh(&path);
-				ih = get_ih(&path);
-				item = get_item(&path);
-				item_pos = path.pos_in_item;
-
-				RFALSE(!is_indirect_le_ih(ih),
-				       "green-9003: indirect item expected");
-			}
-
-			/* See if there is some block associated with the file
-			   at that position, map the buffer to this block */
-			if (get_block_num(item, item_pos)) {
-				map_bh(bh, inode->i_sb,
-				       get_block_num(item, item_pos));
-				blocks--;	// Decrease the amount of blocks that need to be
-				// allocated
-			}
-			item_pos++;
-			/* Update the key */
-			set_cpu_key_k_offset(&key,
-					     cpu_key_k_offset(&key) +
-					     inode->i_sb->s_blocksize);
-		}
-	}
-	pathrelse(&path);	// Free the path
-	reiserfs_write_unlock(inode->i_sb);
-
-	/* Now zero out unmappend buffers for the first and last pages of
-	   write area or issue read requests if page is mapped. */
-	/* First page, see if it is not uptodate */
-	if (!PageUptodate(prepared_pages[0])) {
-		head = page_buffers(prepared_pages[0]);
-
-		/* For each buffer in page */
-		for (bh = head, block_start = 0; bh != head || !block_start;
-		     block_start = block_end, bh = bh->b_this_page) {
-
-			if (!bh)
-				reiserfs_panic(inode->i_sb,
-					       "green-9002: Allocated but absent buffer for a page?");
-			/* Find where this buffer ends */
-			block_end = block_start + inode->i_sb->s_blocksize;
-			if (block_end <= from)
-				/* if this buffer is before requested data to map, skip it */
-				continue;
-			if (block_start < from) {	/* Aha, our partial buffer */
-				if (buffer_mapped(bh)) {	/* If it is mapped, we need to
-								   issue READ request for it to
-								   not loose data */
-					ll_rw_block(READ, 1, &bh);
-					*wait_bh++ = bh;
-				} else {	/* Not mapped, zero it */
-					zero_user_page(prepared_pages[0],
-						       block_start,
-						       from - block_start, KM_USER0);
-					set_buffer_uptodate(bh);
-				}
-			}
-		}
-	}
-
-	/* Last page, see if it is not uptodate, or if the last page is past the end of the file. */
-	if (!PageUptodate(prepared_pages[num_pages - 1]) ||
-	    ((pos + write_bytes) >> PAGE_CACHE_SHIFT) >
-	    (inode->i_size >> PAGE_CACHE_SHIFT)) {
-		head = page_buffers(prepared_pages[num_pages - 1]);
-
-		/* for each buffer in page */
-		for (bh = head, block_start = 0; bh != head || !block_start;
-		     block_start = block_end, bh = bh->b_this_page) {
-
-			if (!bh)
-				reiserfs_panic(inode->i_sb,
-					       "green-9002: Allocated but absent buffer for a page?");
-			/* Find where this buffer ends */
-			block_end = block_start + inode->i_sb->s_blocksize;
-			if (block_start >= to)
-				/* if this buffer is after requested data to map, skip it */
-				break;
-			if (block_end > to) {	/* Aha, our partial buffer */
-				if (buffer_mapped(bh)) {	/* If it is mapped, we need to
-								   issue READ request for it to
-								   not loose data */
-					ll_rw_block(READ, 1, &bh);
-					*wait_bh++ = bh;
-				} else {	/* Not mapped, zero it */
-					zero_user_page(prepared_pages[num_pages-1],
-							to, block_end - to, KM_USER0);
-					set_buffer_uptodate(bh);
-				}
-			}
-		}
-	}
-
-	/* Wait for read requests we made to happen, if necessary */
-	while (wait_bh > wait) {
-		wait_on_buffer(*--wait_bh);
-		if (!buffer_uptodate(*wait_bh)) {
-			res = -EIO;
-			goto failed_read;
-		}
-	}
-
-	return blocks;
-      failed_page_grabbing:
-	num_pages = i;
-      failed_read:
-	reiserfs_unprepare_pages(prepared_pages, num_pages);
-	return res;
-}
-
 /* Write @count bytes at position @ppos in a file indicated by @file
    from the buffer @buf.  
 
@@ -1284,14 +256,9 @@ static ssize_t reiserfs_file_write(struc
 							 * new current position before returning. */
 				   )
 {
-	size_t already_written = 0;	// Number of bytes already written to the file.
-	loff_t pos;		// Current position in the file.
-	ssize_t res;		// return value of various functions that we call.
-	int err = 0;
 	struct inode *inode = file->f_path.dentry->d_inode;	// Inode of the file that we are writing to.
 	/* To simplify coding at this time, we store
 	   locked pages in array for now */
-	struct page *prepared_pages[REISERFS_WRITE_PAGES_AT_A_TIME];
 	struct reiserfs_transaction_handle th;
 	th.t_trans_id = 0;
 
@@ -1312,212 +279,7 @@ static ssize_t reiserfs_file_write(struc
 			count = MAX_NON_LFS - (unsigned long)*ppos;
 	}
 
-	if (file->f_flags & O_DIRECT)
-		return do_sync_write(file, buf, count, ppos);
-
-	if (unlikely((ssize_t) count < 0))
-		return -EINVAL;
-
-	if (unlikely(!access_ok(VERIFY_READ, buf, count)))
-		return -EFAULT;
-
-	mutex_lock(&inode->i_mutex);	// locks the entire file for just us
-
-	pos = *ppos;
-
-	/* Check if we can write to specified region of file, file
-	   is not overly big and this kind of stuff. Adjust pos and
-	   count, if needed */
-	res = generic_write_checks(file, &pos, &count, 0);
-	if (res)
-		goto out;
-
-	if (count == 0)
-		goto out;
-
-	res = remove_suid(file->f_path.dentry);
-	if (res)
-		goto out;
-
-	file_update_time(file);
-
-	// Ok, we are done with all the checks.
-
-	// Now we should start real work
-
-	/* If we are going to write past the file's packed tail or if we are going
-	   to overwrite part of the tail, we need that tail to be converted into
-	   unformatted node */
-	res = reiserfs_check_for_tail_and_convert(inode, pos, count);
-	if (res)
-		goto out;
-
-	while (count > 0) {
-		/* This is the main loop in which we running until some error occures
-		   or until we write all of the data. */
-		size_t num_pages;	/* amount of pages we are going to write this iteration */
-		size_t write_bytes;	/* amount of bytes to write during this iteration */
-		size_t blocks_to_allocate;	/* how much blocks we need to allocate for this iteration */
-
-		/*  (pos & (PAGE_CACHE_SIZE-1)) is an idiom for offset into a page of pos */
-		num_pages = !!((pos + count) & (PAGE_CACHE_SIZE - 1)) +	/* round up partial
-									   pages */
-		    ((count +
-		      (pos & (PAGE_CACHE_SIZE - 1))) >> PAGE_CACHE_SHIFT);
-		/* convert size to amount of
-		   pages */
-		reiserfs_write_lock(inode->i_sb);
-		if (num_pages > REISERFS_WRITE_PAGES_AT_A_TIME
-		    || num_pages > reiserfs_can_fit_pages(inode->i_sb)) {
-			/* If we were asked to write more data than we want to or if there
-			   is not that much space, then we shorten amount of data to write
-			   for this iteration. */
-			num_pages =
-			    min_t(size_t, REISERFS_WRITE_PAGES_AT_A_TIME,
-				  reiserfs_can_fit_pages(inode->i_sb));
-			/* Also we should not forget to set size in bytes accordingly */
-			write_bytes = (num_pages << PAGE_CACHE_SHIFT) -
-			    (pos & (PAGE_CACHE_SIZE - 1));
-			/* If position is not on the
-			   start of the page, we need
-			   to substract the offset
-			   within page */
-		} else
-			write_bytes = count;
-
-		/* reserve the blocks to be allocated later, so that later on
-		   we still have the space to write the blocks to */
-		reiserfs_claim_blocks_to_be_allocated(inode->i_sb,
-						      num_pages <<
-						      (PAGE_CACHE_SHIFT -
-						       inode->i_blkbits));
-		reiserfs_write_unlock(inode->i_sb);
-
-		if (!num_pages) {	/* If we do not have enough space even for a single page... */
-			if (pos >
-			    inode->i_size + inode->i_sb->s_blocksize -
-			    (pos & (inode->i_sb->s_blocksize - 1))) {
-				res = -ENOSPC;
-				break;	// In case we are writing past the end of the last file block, break.
-			}
-			// Otherwise we are possibly overwriting the file, so
-			// let's set write size to be equal or less than blocksize.
-			// This way we get it correctly for file holes.
-			// But overwriting files on absolutelly full volumes would not
-			// be very efficient. Well, people are not supposed to fill
-			// 100% of disk space anyway.
-			write_bytes =
-			    min_t(size_t, count,
-				  inode->i_sb->s_blocksize -
-				  (pos & (inode->i_sb->s_blocksize - 1)));
-			num_pages = 1;
-			// No blocks were claimed before, so do it now.
-			reiserfs_claim_blocks_to_be_allocated(inode->i_sb,
-							      1 <<
-							      (PAGE_CACHE_SHIFT
-							       -
-							       inode->
-							       i_blkbits));
-		}
-
-		/* Prepare for writing into the region, read in all the
-		   partially overwritten pages, if needed. And lock the pages,
-		   so that nobody else can access these until we are done.
-		   We get number of actual blocks needed as a result. */
-		res = reiserfs_prepare_file_region_for_write(inode, pos,
-							     num_pages,
-							     write_bytes,
-							     prepared_pages);
-		if (res < 0) {
-			reiserfs_release_claimed_blocks(inode->i_sb,
-							num_pages <<
-							(PAGE_CACHE_SHIFT -
-							 inode->i_blkbits));
-			break;
-		}
-
-		blocks_to_allocate = res;
-
-		/* First we correct our estimate of how many blocks we need */
-		reiserfs_release_claimed_blocks(inode->i_sb,
-						(num_pages <<
-						 (PAGE_CACHE_SHIFT -
-						  inode->i_sb->
-						  s_blocksize_bits)) -
-						blocks_to_allocate);
-
-		if (blocks_to_allocate > 0) {	/*We only allocate blocks if we need to */
-			/* Fill in all the possible holes and append the file if needed */
-			res =
-			    reiserfs_allocate_blocks_for_region(&th, inode, pos,
-								num_pages,
-								write_bytes,
-								prepared_pages,
-								blocks_to_allocate);
-		}
-
-		/* well, we have allocated the blocks, so it is time to free
-		   the reservation we made earlier. */
-		reiserfs_release_claimed_blocks(inode->i_sb,
-						blocks_to_allocate);
-		if (res) {
-			reiserfs_unprepare_pages(prepared_pages, num_pages);
-			break;
-		}
-
-/* NOTE that allocating blocks and filling blocks can be done in reverse order
-   and probably we would do that just to get rid of garbage in files after a
-   crash */
-
-		/* Copy data from user-supplied buffer to file's pages */
-		res =
-		    reiserfs_copy_from_user_to_file_region(pos, num_pages,
-							   write_bytes,
-							   prepared_pages, buf);
-		if (res) {
-			reiserfs_unprepare_pages(prepared_pages, num_pages);
-			break;
-		}
-
-		/* Send the pages to disk and unlock them. */
-		res =
-		    reiserfs_submit_file_region_for_write(&th, inode, pos,
-							  num_pages,
-							  write_bytes,
-							  prepared_pages);
-		if (res)
-			break;
-
-		already_written += write_bytes;
-		buf += write_bytes;
-		*ppos = pos += write_bytes;
-		count -= write_bytes;
-		balance_dirty_pages_ratelimited_nr(inode->i_mapping, num_pages);
-	}
-
-	/* this is only true on error */
-	if (th.t_trans_id) {
-		reiserfs_write_lock(inode->i_sb);
-		err = journal_end(&th, th.t_super, th.t_blocks_allocated);
-		reiserfs_write_unlock(inode->i_sb);
-		if (err) {
-			res = err;
-			goto out;
-		}
-	}
-
-	if (likely(res >= 0) &&
-	    (unlikely((file->f_flags & O_SYNC) || IS_SYNC(inode))))
-		res = generic_osync_inode(inode, file->f_mapping,
-		                          OSYNC_METADATA | OSYNC_DATA);
-
-	mutex_unlock(&inode->i_mutex);
-	reiserfs_async_progress_wait(inode->i_sb);
-	return (already_written != 0) ? already_written : res;
-
-      out:
-	mutex_unlock(&inode->i_mutex);	// unlock the file on exit.
-	return res;
+	return do_sync_write(file, buf, count, ppos);
 }
 
 const struct file_operations reiserfs_file_operations = {
#41 lore raw

PATCH 29/41 reiserfs convert to new aops.

In reply to [email protected]

From: Vladimir Saveliev <[email protected]>

Convert reiserfs to new aops

---

---
 fs/reiserfs/inode.c |  177 +++++++++++++++++++++++++++++++++++++++++++++++++---
 fs/reiserfs/ioctl.c |   10 +-
 fs/reiserfs/xattr.c |   16 +++-
 3 files changed, 184 insertions(+), 19 deletions(-)
fs/reiserfs/inode.c +167 -10
Index: linux-2.6/fs/reiserfs/inode.c
===================================================================
@@ -17,11 +17,12 @@
 #include <linux/mpage.h>
 #include <linux/writeback.h>
 #include <linux/quotaops.h>
+#include <linux/swap.h>
 
-static int reiserfs_commit_write(struct file *f, struct page *page,
-				 unsigned from, unsigned to);
-static int reiserfs_prepare_write(struct file *f, struct page *page,
-				  unsigned from, unsigned to);
+int reiserfs_commit_write(struct file *f, struct page *page,
+			  unsigned from, unsigned to);
+int reiserfs_prepare_write(struct file *f, struct page *page,
+			   unsigned from, unsigned to);
 
 void reiserfs_delete_inode(struct inode *inode)
 {
@@ -2550,8 +2551,71 @@ static int reiserfs_writepage(struct pag
 	return reiserfs_write_full_page(page, wbc);
 }
 
-static int reiserfs_prepare_write(struct file *f, struct page *page,
-				  unsigned from, unsigned to)
+static int reiserfs_write_begin(struct file *file,
+				struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata)
+{
+	struct inode *inode;
+	struct page *page;
+	pgoff_t index;
+	int ret;
+	int old_ref = 0;
+
+	index = pos >> PAGE_CACHE_SHIFT;
+	page = __grab_cache_page(mapping, index);
+	if (!page)
+		return -ENOMEM;
+	*pagep = page;
+
+	inode = mapping->host;
+	reiserfs_wait_on_write_block(inode->i_sb);
+	fix_tail_page_for_writing(page);
+	if (reiserfs_transaction_running(inode->i_sb)) {
+		struct reiserfs_transaction_handle *th;
+		th = (struct reiserfs_transaction_handle *)current->
+		    journal_info;
+		BUG_ON(!th->t_refcount);
+		BUG_ON(!th->t_trans_id);
+		old_ref = th->t_refcount;
+		th->t_refcount++;
+	}
+	ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				reiserfs_get_block);
+	if (ret && reiserfs_transaction_running(inode->i_sb)) {
+		struct reiserfs_transaction_handle *th = current->journal_info;
+		/* this gets a little ugly.  If reiserfs_get_block returned an
+		 * error and left a transacstion running, we've got to close it,
+		 * and we've got to free handle if it was a persistent transaction.
+		 *
+		 * But, if we had nested into an existing transaction, we need
+		 * to just drop the ref count on the handle.
+		 *
+		 * If old_ref == 0, the transaction is from reiserfs_get_block,
+		 * and it was a persistent trans.  Otherwise, it was nested above.
+		 */
+		if (th->t_refcount > old_ref) {
+			if (old_ref)
+				th->t_refcount--;
+			else {
+				int err;
+				reiserfs_write_lock(inode->i_sb);
+				err = reiserfs_end_persistent_transaction(th);
+				reiserfs_write_unlock(inode->i_sb);
+				if (err)
+					ret = err;
+			}
+		}
+	}
+	if (ret) {
+		unlock_page(page);
+		page_cache_release(page);
+	}
+	return ret;
+}
+
+int reiserfs_prepare_write(struct file *f, struct page *page,
+			   unsigned from, unsigned to)
 {
 	struct inode *inode = page->mapping->host;
 	int ret;
@@ -2604,8 +2668,101 @@ static sector_t reiserfs_aop_bmap(struct
 	return generic_block_bmap(as, block, reiserfs_bmap);
 }
 
-static int reiserfs_commit_write(struct file *f, struct page *page,
-				 unsigned from, unsigned to)
+static int reiserfs_write_end(struct file *file, struct address_space *mapping,
+			      loff_t pos, unsigned len, unsigned copied,
+			      struct page *page, void *fsdata)
+{
+	struct inode *inode = page->mapping->host;
+	int ret = 0;
+	int update_sd = 0;
+	struct reiserfs_transaction_handle *th;
+	unsigned start;
+
+
+	reiserfs_wait_on_write_block(inode->i_sb);
+	if (reiserfs_transaction_running(inode->i_sb))
+		th = current->journal_info;
+	else
+		th = NULL;
+
+	start = pos & (PAGE_CACHE_SIZE - 1);
+	if (unlikely(copied < len)) {
+		if (!PageUptodate(page))
+			copied = 0;
+
+		page_zero_new_buffers(page, start + copied, start + len);
+	}
+	flush_dcache_page(page);
+
+	reiserfs_commit_page(inode, page, start, start + copied);
+	unlock_page(page);
+	mark_page_accessed(page);
+	page_cache_release(page);
+
+	/* generic_commit_write does this for us, but does not update the
+	 ** transaction tracking stuff when the size changes.  So, we have
+	 ** to do the i_size updates here.
+	 */
+	pos += copied;
+	if (pos > inode->i_size) {
+		struct reiserfs_transaction_handle myth;
+		reiserfs_write_lock(inode->i_sb);
+		/* If the file have grown beyond the border where it
+		   can have a tail, unmark it as needing a tail
+		   packing */
+		if ((have_large_tails(inode->i_sb)
+		     && inode->i_size > i_block_size(inode) * 4)
+		    || (have_small_tails(inode->i_sb)
+			&& inode->i_size > i_block_size(inode)))
+			REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask;
+
+		ret = journal_begin(&myth, inode->i_sb, 1);
+		if (ret) {
+			reiserfs_write_unlock(inode->i_sb);
+			goto journal_error;
+		}
+		reiserfs_update_inode_transaction(inode);
+		inode->i_size = pos;
+		/*
+		 * this will just nest into our transaction.  It's important
+		 * to use mark_inode_dirty so the inode gets pushed around on the
+		 * dirty lists, and so that O_SYNC works as expected
+		 */
+		mark_inode_dirty(inode);
+		reiserfs_update_sd(&myth, inode);
+		update_sd = 1;
+		ret = journal_end(&myth, inode->i_sb, 1);
+		reiserfs_write_unlock(inode->i_sb);
+		if (ret)
+			goto journal_error;
+	}
+	if (th) {
+		reiserfs_write_lock(inode->i_sb);
+		if (!update_sd)
+			mark_inode_dirty(inode);
+		ret = reiserfs_end_persistent_transaction(th);
+		reiserfs_write_unlock(inode->i_sb);
+		if (ret)
+			goto out;
+	}
+
+      out:
+	return ret == 0 ? copied : ret;
+
+      journal_error:
+	if (th) {
+		reiserfs_write_lock(inode->i_sb);
+		if (!update_sd)
+			reiserfs_update_sd(th, inode);
+		ret = reiserfs_end_persistent_transaction(th);
+		reiserfs_write_unlock(inode->i_sb);
+	}
+
+	return ret;
+}
+
+int reiserfs_commit_write(struct file *f, struct page *page,
+			  unsigned from, unsigned to)
 {
 	struct inode *inode = page->mapping->host;
 	loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
@@ -2999,8 +3156,8 @@ const struct address_space_operations re
 	.releasepage = reiserfs_releasepage,
 	.invalidatepage = reiserfs_invalidatepage,
 	.sync_page = block_sync_page,
-	.prepare_write = reiserfs_prepare_write,
-	.commit_write = reiserfs_commit_write,
+	.write_begin = reiserfs_write_begin,
+	.write_end = reiserfs_write_end,
 	.bmap = reiserfs_aop_bmap,
 	.direct_IO = reiserfs_direct_IO,
 	.set_page_dirty = reiserfs_set_page_dirty,
fs/reiserfs/ioctl.c +6 -4
Index: linux-2.6/fs/reiserfs/ioctl.c
===================================================================
@@ -129,6 +129,10 @@ long reiserfs_compat_ioctl(struct file *
 }
 #endif
 
+int reiserfs_commit_write(struct file *f, struct page *page,
+			  unsigned from, unsigned to);
+int reiserfs_prepare_write(struct file *f, struct page *page,
+			   unsigned from, unsigned to);
 /*
 ** reiserfs_unpack
 ** Function try to convert tail from direct item into indirect.
@@ -176,15 +180,13 @@ static int reiserfs_unpack(struct inode 
 	if (!page) {
 		goto out;
 	}
-	retval =
-	    mapping->a_ops->prepare_write(NULL, page, write_from, write_from);
+	retval = reiserfs_prepare_write(NULL, page, write_from, write_from);
 	if (retval)
 		goto out_unlock;
 
 	/* conversion can change page contents, must flush */
 	flush_dcache_page(page);
-	retval =
-	    mapping->a_ops->commit_write(NULL, page, write_from, write_from);
+	retval = reiserfs_commit_write(NULL, page, write_from, write_from);
 	REISERFS_I(inode)->i_flags |= i_nopack_mask;
 
       out_unlock:
fs/reiserfs/xattr.c +11 -5
Index: linux-2.6/fs/reiserfs/xattr.c
===================================================================
@@ -426,6 +426,12 @@ static inline __u32 xattr_hash(const cha
 	return csum_partial(msg, len, 0);
 }
 
+int reiserfs_commit_write(struct file *f, struct page *page,
+			  unsigned from, unsigned to);
+int reiserfs_prepare_write(struct file *f, struct page *page,
+			   unsigned from, unsigned to);
+
+
 /* Generic extended attribute operations that can be used by xa plugins */
 
 /*
@@ -512,15 +518,15 @@ reiserfs_xattr_set(struct inode *inode, 
 			rxh->h_hash = cpu_to_le32(xahash);
 		}
 
-		err = mapping->a_ops->prepare_write(fp, page, page_offset,
-						    page_offset + chunk + skip);
+		err = reiserfs_prepare_write(fp, page, page_offset,
+					    page_offset + chunk + skip);
 		if (!err) {
 			if (buffer)
 				memcpy(data + skip, buffer + buffer_pos, chunk);
 			err =
-			    mapping->a_ops->commit_write(fp, page, page_offset,
-							 page_offset + chunk +
-							 skip);
+			    reiserfs_commit_write(fp, page, page_offset,
+						  page_offset + chunk +
+						  skip);
 		}
 		unlock_page(page);
 		reiserfs_put_page(page);
#42 lore raw

PATCH 30/41 reiserfs use generic_cont_expand_simple

In reply to [email protected]

From: Vladimir Saveliev <[email protected]>

This patch makes reiserfs to use AOP_FLAG_CONT_EXPAND in order to get rid of the special generic_cont_expand routine

---

---
 fs/reiserfs/inode.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)
fs/reiserfs/inode.c +11 -2
Index: linux-2.6/fs/reiserfs/inode.c
===================================================================
@@ -2562,13 +2562,20 @@ static int reiserfs_write_begin(struct f
 	int ret;
 	int old_ref = 0;
 
+ 	inode = mapping->host;
+	*fsdata = 0;
+ 	if (flags & AOP_FLAG_CONT_EXPAND &&
+ 	    (pos & (inode->i_sb->s_blocksize - 1)) == 0) {
+ 		pos ++;
+		*fsdata = (void *)(unsigned long)flags;
+	}
+
 	index = pos >> PAGE_CACHE_SHIFT;
 	page = __grab_cache_page(mapping, index);
 	if (!page)
 		return -ENOMEM;
 	*pagep = page;
 
-	inode = mapping->host;
 	reiserfs_wait_on_write_block(inode->i_sb);
 	fix_tail_page_for_writing(page);
 	if (reiserfs_transaction_running(inode->i_sb)) {
@@ -2678,6 +2685,8 @@ static int reiserfs_write_end(struct fil
 	struct reiserfs_transaction_handle *th;
 	unsigned start;
 
+	if ((unsigned long)fsdata & AOP_FLAG_CONT_EXPAND)
+		pos ++;
 
 	reiserfs_wait_on_write_block(inode->i_sb);
 	if (reiserfs_transaction_running(inode->i_sb))
@@ -3066,7 +3075,7 @@ int reiserfs_setattr(struct dentry *dent
 		}
 		/* fill in hole pointers in the expanding truncate case. */
 		if (attr->ia_size > inode->i_size) {
-			error = generic_cont_expand(inode, attr->ia_size);
+			error = generic_cont_expand_simple(inode, attr->ia_size);
 			if (REISERFS_I(inode)->i_prealloc_count > 0) {
 				int err;
 				struct reiserfs_transaction_handle th;
#43 lore raw

PATCH 31/41 With reiserfs no longer using the weird generic_cont_expand, remove it completely.

In reply to [email protected]
---
 fs/buffer.c                 |   20 --------------------
 include/linux/buffer_head.h |    1 -
 2 files changed, 21 deletions(-)
fs/buffer.c +0 -20
Index: linux-2.6/fs/buffer.c
===================================================================
@@ -2189,25 +2189,6 @@ out:
 	return err;
 }
 
-int generic_cont_expand(struct inode *inode, loff_t size)
-{
-	unsigned int offset;
-
-	offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
-
-	/* ugh.  in prepare/commit_write, if from==to==start of block, we
-	 * skip the prepare.  make sure we never send an offset for the start
-	 * of a block.
-	 * XXX: actually, this should be handled in those filesystems by
-	 * checking for the AOP_FLAG_CONT_EXPAND flag.
-	 */
-	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
-		/* caller must handle this extra byte. */
-		size++;
-	}
-	return generic_cont_expand_simple(inode, size);
-}
-
 int cont_expand_zero(struct file *file, struct address_space *mapping,
 			loff_t pos, loff_t *bytes)
 {
@@ -3135,7 +3116,6 @@ EXPORT_SYMBOL(file_fsync);
 EXPORT_SYMBOL(fsync_bdev);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_commit_write);
-EXPORT_SYMBOL(generic_cont_expand);
 EXPORT_SYMBOL(generic_cont_expand_simple);
 EXPORT_SYMBOL(init_buffer);
 EXPORT_SYMBOL(invalidate_bdev);
include/linux/buffer_head.h +0 -1
Index: linux-2.6/include/linux/buffer_head.h
===================================================================
@@ -217,7 +217,6 @@ int block_prepare_write(struct page*, un
 int cont_write_begin(struct file *, struct address_space *, loff_t,
 			unsigned, unsigned, struct page **, void **,
 			get_block_t *, loff_t *);
-int generic_cont_expand(struct inode *inode, loff_t size);
 int generic_cont_expand_simple(struct inode *inode, loff_t size);
 int block_commit_write(struct page *page, unsigned from, unsigned to);
 void block_sync_page(struct page *);
#44 lore raw

PATCH 32/41 nfs convert to new aops.

In reply to [email protected]
 fs/nfs/file.c |   49 ++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 36 insertions(+), 13 deletions(-)
fs/nfs/file.c +36 -13
Index: linux-2.6/fs/nfs/file.c
===================================================================
@@ -283,27 +283,50 @@ nfs_fsync(struct file *file, struct dent
 }
 
 /*
- * This does the "real" work of the write. The generic routine has
- * allocated the page, locked it, done all the page alignment stuff
- * calculations etc. Now we should just copy the data from user
- * space and write it back to the real medium..
+ * This does the "real" work of the write. We must allocate and lock the
+ * page to be sent back to the generic routine, which then copies the
+ * data from user space.
  *
  * If the writer ends up delaying the write, the writer needs to
  * increment the page use counts until he is done with the page.
  */
-static int nfs_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
-{
-	return nfs_flush_incompatible(file, page);
+static int nfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	int ret;
+	pgoff_t index;
+	struct page *page;
+	index = pos >> PAGE_CACHE_SHIFT;
+
+	page = __grab_cache_page(mapping, index);
+	if (!page)
+		return -ENOMEM;
+	*pagep = page;
+
+	ret = nfs_flush_incompatible(file, page);
+	if (ret) {
+		unlock_page(page);
+		page_cache_release(page);
+	}
+	return ret;
 }
 
-static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+static int nfs_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
-	long status;
+	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
+	int status;
 
 	lock_kernel();
-	status = nfs_updatepage(file, page, offset, to-offset);
+	status = nfs_updatepage(file, page, offset, copied);
 	unlock_kernel();
-	return status;
+
+	unlock_page(page);
+	page_cache_release(page);
+
+	return status < 0 ? status : copied;
 }
 
 static void nfs_invalidate_page(struct page *page, unsigned long offset)
@@ -331,8 +354,8 @@ const struct address_space_operations nf
 	.set_page_dirty = nfs_set_page_dirty,
 	.writepage = nfs_writepage,
 	.writepages = nfs_writepages,
-	.prepare_write = nfs_prepare_write,
-	.commit_write = nfs_commit_write,
+	.write_begin = nfs_write_begin,
+	.write_end = nfs_write_end,
 	.invalidatepage = nfs_invalidate_page,
 	.releasepage = nfs_release_page,
 #ifdef CONFIG_NFS_DIRECTIO
#45 lore raw

PATCH 33/41 smb convert to new aops.

In reply to [email protected]
Cc Linux Filesystems <[email protected]>
 fs/smbfs/file.c |   34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)
fs/smbfs/file.c +25 -9
Index: linux-2.6/fs/smbfs/file.c
===================================================================
@@ -291,29 +291,45 @@ out:
  * If the writer ends up delaying the write, the writer needs to
  * increment the page use counts until he is done with the page.
  */
-static int smb_prepare_write(struct file *file, struct page *page, 
-			     unsigned offset, unsigned to)
-{
+static int smb_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	*pagep = __grab_cache_page(mapping, index);
+	if (!*pagep)
+		return -ENOMEM;
 	return 0;
 }
 
-static int smb_commit_write(struct file *file, struct page *page,
-			    unsigned offset, unsigned to)
+static int smb_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
 	int status;
+	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
 
-	status = -EFAULT;
 	lock_kernel();
-	status = smb_updatepage(file, page, offset, to-offset);
+	status = smb_updatepage(file, page, offset, copied);
 	unlock_kernel();
+
+	if (!status) {
+		if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
+			SetPageUptodate(page);
+		status = copied;
+	}
+
+	unlock_page(page);
+	page_cache_release(page);
+
 	return status;
 }
 
 const struct address_space_operations smb_file_aops = {
 	.readpage = smb_readpage,
 	.writepage = smb_writepage,
-	.prepare_write = smb_prepare_write,
-	.commit_write = smb_commit_write
+	.write_begin = smb_write_begin,
+	.write_end = smb_write_end,
 };
 
 /* 
#46 lore raw

PATCH 34/41 fuse convert to new aops.

In reply to [email protected]

[mszeredi]
- don't send zero length write requests
- it is not legal for the filesystem to return with zero written bytes

 fs/fuse/file.c |   48 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 15 deletions(-)
fs/fuse/file.c +33 -15
Index: linux-2.6/fs/fuse/file.c
===================================================================
@@ -444,22 +444,25 @@ static size_t fuse_send_write(struct fus
 	return outarg.size;
 }
 
-static int fuse_prepare_write(struct file *file, struct page *page,
-			      unsigned offset, unsigned to)
-{
-	/* No op */
+static int fuse_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+	*pagep = __grab_cache_page(mapping, index);
+	if (!*pagep)
+		return -ENOMEM;
 	return 0;
 }
 
-static int fuse_commit_write(struct file *file, struct page *page,
-			     unsigned offset, unsigned to)
+static int fuse_buffered_write(struct file *file, struct inode *inode,
+			       loff_t pos, unsigned count, struct page *page)
 {
 	int err;
 	size_t nres;
-	unsigned count = to - offset;
-	struct inode *inode = page->mapping->host;
 	struct fuse_conn *fc = get_fuse_conn(inode);
-	loff_t pos = page_offset(page) + offset;
+	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
 	struct fuse_req *req;
 
 	if (is_bad_inode(inode))
@@ -475,20 +478,35 @@ static int fuse_commit_write(struct file
 	nres = fuse_send_write(req, file, inode, pos, count);
 	err = req->out.h.error;
 	fuse_put_request(fc, req);
-	if (!err && nres != count)
+	if (!err && !nres)
 		err = -EIO;
 	if (!err) {
-		pos += count;
+		pos += nres;
 		spin_lock(&fc->lock);
 		if (pos > inode->i_size)
 			i_size_write(inode, pos);
 		spin_unlock(&fc->lock);
 
-		if (offset == 0 && to == PAGE_CACHE_SIZE)
+		if (count == PAGE_CACHE_SIZE)
 			SetPageUptodate(page);
 	}
 	fuse_invalidate_attr(inode);
-	return err;
+	return err ? err : nres;
+}
+
+static int fuse_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
+{
+	struct inode *inode = mapping->host;
+	int res = 0;
+
+	if (copied)
+		res = fuse_buffered_write(file, inode, pos, copied, page);
+
+	unlock_page(page);
+	page_cache_release(page);
+	return res;
 }
 
 static void fuse_release_user_pages(struct fuse_req *req, int write)
@@ -819,8 +837,8 @@ static const struct file_operations fuse
 
 static const struct address_space_operations fuse_file_aops  = {
 	.readpage	= fuse_readpage,
-	.prepare_write	= fuse_prepare_write,
-	.commit_write	= fuse_commit_write,
+	.write_begin	= fuse_write_begin,
+	.write_end	= fuse_write_end,
 	.readpages	= fuse_readpages,
 	.set_page_dirty	= fuse_set_page_dirty,
 	.bmap		= fuse_bmap,
#47 lore raw

PATCH 35/41 hostfs convert to new aops.

In reply to [email protected]

This also gets rid of a lot of useless read_file stuff. And also optimises the full page write case by marking a !uptodate page uptodate.

Cc Jeff Dike <[email protected]>
Cc Linux Filesystems <[email protected]>
 fs/hostfs/hostfs_kern.c |   70 +++++++++++++++++++-----------------------------
 1 file changed, 28 insertions(+), 42 deletions(-)
fs/hostfs/hostfs_kern.c +28 -42
Index: linux-2.6/fs/hostfs/hostfs_kern.c
===================================================================
@@ -466,56 +466,42 @@ int hostfs_readpage(struct file *file, s
 	return err;
 }
 
-int hostfs_prepare_write(struct file *file, struct page *page,
-			 unsigned int from, unsigned int to)
+int hostfs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	char *buffer;
-	long long start, tmp;
-	int err;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
 
-	start = (long long) page->index << PAGE_CACHE_SHIFT;
-	buffer = kmap(page);
-	if(from != 0){
-		tmp = start;
-		err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer,
-				from);
-		if(err < 0) goto out;
-	}
-	if(to != PAGE_CACHE_SIZE){
-		start += to;
-		err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to,
-				PAGE_CACHE_SIZE - to);
-		if(err < 0) goto out;
-	}
-	err = 0;
- out:
-	kunmap(page);
-	return err;
+	*pagep = __grab_cache_page(mapping, index);
+	if (!*pagep)
+		return -ENOMEM;
+	return 0;
 }
 
-int hostfs_commit_write(struct file *file, struct page *page, unsigned from,
-		 unsigned to)
+int hostfs_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
-	struct address_space *mapping = page->mapping;
 	struct inode *inode = mapping->host;
-	char *buffer;
-	long long start;
-	int err = 0;
+	void *buffer;
+	unsigned from = pos & (PAGE_CACHE_SIZE - 1);
+	int err;
 
-	start = (((long long) page->index) << PAGE_CACHE_SHIFT) + from;
 	buffer = kmap(page);
-	err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from,
-			 to - from);
-	if(err > 0) err = 0;
-
-	/* Actually, if !err, write_file has added to-from to start, so, despite
-	 * the appearance, we are comparing i_size against the _last_ written
-	 * location, as we should. */
+	err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied);
+	kunmap(page);
 
-	if(!err && (start > inode->i_size))
-		inode->i_size = start;
+	if (!PageUptodate(page) && err == PAGE_CACHE_SIZE)
+		SetPageUptodate(page);
+	unlock_page(page);
+	page_cache_release(page);
+
+	/* If err > 0, write_file has added err to pos, so we are comparing
+	 * i_size against the last byte written.
+	 */
+	if (err > 0 && (pos > inode->i_size))
+		inode->i_size = pos;
 
-	kunmap(page);
 	return err;
 }
 
@@ -523,8 +509,8 @@ static const struct address_space_operat
 	.writepage 	= hostfs_writepage,
 	.readpage	= hostfs_readpage,
 	.set_page_dirty = __set_page_dirty_nobuffers,
-	.prepare_write	= hostfs_prepare_write,
-	.commit_write	= hostfs_commit_write
+	.write_begin	= hostfs_write_begin,
+	.write_end	= hostfs_write_end,
 };
 
 static int init_inode(struct inode *inode, struct dentry *dentry)
#48 lore raw

PATCH 36/41 jffs2 convert to new aops.

In reply to [email protected]
 fs/jffs2/file.c |  105 +++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 66 insertions(+), 39 deletions(-)
fs/jffs2/file.c +66 -39
Index: linux-2.6/fs/jffs2/file.c
===================================================================
@@ -19,10 +19,12 @@
 #include <linux/jffs2.h>
 #include "nodelist.h"
 
-static int jffs2_commit_write (struct file *filp, struct page *pg,
-			       unsigned start, unsigned end);
-static int jffs2_prepare_write (struct file *filp, struct page *pg,
-				unsigned start, unsigned end);
+static int jffs2_write_end(struct file *filp, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *pg, void *fsdata);
+static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata);
 static int jffs2_readpage (struct file *filp, struct page *pg);
 
 int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
@@ -65,8 +67,8 @@ const struct inode_operations jffs2_file
 const struct address_space_operations jffs2_file_address_operations =
 {
 	.readpage =	jffs2_readpage,
-	.prepare_write =jffs2_prepare_write,
-	.commit_write =	jffs2_commit_write
+	.write_begin =	jffs2_write_begin,
+	.write_end =	jffs2_write_end,
 };
 
 static int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
@@ -119,15 +121,23 @@ static int jffs2_readpage (struct file *
 	return ret;
 }
 
-static int jffs2_prepare_write (struct file *filp, struct page *pg,
-				unsigned start, unsigned end)
+static int jffs2_write_begin(struct file *filp, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	struct inode *inode = pg->mapping->host;
+	struct page *pg;
+	struct inode *inode = mapping->host;
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-	uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
+	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+	uint32_t pageofs = pos & (PAGE_CACHE_SIZE - 1);
 	int ret = 0;
 
-	D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
+	pg = __grab_cache_page(mapping, index);
+	if (!pg)
+		return -ENOMEM;
+	*pagep = pg;
+
+	D1(printk(KERN_DEBUG "jffs2_write_begin()\n"));
 
 	if (pageofs > inode->i_size) {
 		/* Make new hole frag from old EOF to new page */
@@ -142,7 +152,7 @@ static int jffs2_prepare_write (struct f
 		ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
 					  ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
 		if (ret)
-			return ret;
+			goto out_page;
 
 		down(&f->sem);
 		memset(&ri, 0, sizeof(ri));
@@ -172,7 +182,7 @@ static int jffs2_prepare_write (struct f
 			ret = PTR_ERR(fn);
 			jffs2_complete_reservation(c);
 			up(&f->sem);
-			return ret;
+			goto out_page;
 		}
 		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
 		if (f->metadata) {
@@ -181,65 +191,79 @@ static int jffs2_prepare_write (struct f
 			f->metadata = NULL;
 		}
 		if (ret) {
-			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
+			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in write_begin, returned %d\n", ret));
 			jffs2_mark_node_obsolete(c, fn->raw);
 			jffs2_free_full_dnode(fn);
 			jffs2_complete_reservation(c);
 			up(&f->sem);
-			return ret;
+			goto out_page;
 		}
 		jffs2_complete_reservation(c);
 		inode->i_size = pageofs;
 		up(&f->sem);
 	}
 
-	/* Read in the page if it wasn't already present, unless it's a whole page */
-	if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+	/*
+	 * Read in the page if it wasn't already present. Cannot optimize away
+	 * the whole page write case until jffs2_write_end can handle the
+	 * case of a short-copy.
+	 */
+	if (!PageUptodate(pg)) {
 		down(&f->sem);
 		ret = jffs2_do_readpage_nolock(inode, pg);
 		up(&f->sem);
+		if (ret)
+			goto out_page;
 	}
-	D1(printk(KERN_DEBUG "end prepare_write(). pg->flags %lx\n", pg->flags));
+	D1(printk(KERN_DEBUG "end write_begin(). pg->flags %lx\n", pg->flags));
+	return ret;
+
+out_page:
+	unlock_page(pg);
+	page_cache_release(pg);
 	return ret;
 }
 
-static int jffs2_commit_write (struct file *filp, struct page *pg,
-			       unsigned start, unsigned end)
+static int jffs2_write_end(struct file *filp, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *pg, void *fsdata)
 {
 	/* Actually commit the write from the page cache page we're looking at.
 	 * For now, we write the full page out each time. It sucks, but it's simple
 	 */
-	struct inode *inode = pg->mapping->host;
+	struct inode *inode = mapping->host;
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 	struct jffs2_raw_inode *ri;
+	unsigned start = pos & (PAGE_CACHE_SIZE - 1);
+	unsigned end = start + copied;
 	unsigned aligned_start = start & ~3;
 	int ret = 0;
 	uint32_t writtenlen = 0;
 
-	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
+	D1(printk(KERN_DEBUG "jffs2_write_end(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
 		  inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
 
+	/* We need to avoid deadlock with page_cache_read() in
+	   jffs2_garbage_collect_pass(). So the page must be
+	   up to date to prevent page_cache_read() from trying
+	   to re-lock it. */
+	BUG_ON(!PageUptodate(pg));
+
 	if (end == PAGE_CACHE_SIZE) {
-		if (!start) {
-			/* We need to avoid deadlock with page_cache_read() in
-			   jffs2_garbage_collect_pass(). So we have to mark the
-			   page up to date, to prevent page_cache_read() from
-			   trying to re-lock it. */
-			SetPageUptodate(pg);
-		} else {
-			/* When writing out the end of a page, write out the 
-			   _whole_ page. This helps to reduce the number of
-			   nodes in files which have many short writes, like
-			   syslog files. */
-			start = aligned_start = 0;
-		}
+		/* When writing out the end of a page, write out the
+		   _whole_ page. This helps to reduce the number of
+		   nodes in files which have many short writes, like
+		   syslog files. */
+		start = aligned_start = 0;
 	}
 
 	ri = jffs2_alloc_raw_inode();
 
 	if (!ri) {
-		D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
+		D1(printk(KERN_DEBUG "jffs2_write_end(): Allocation of raw inode failed\n"));
+		unlock_page(pg);
+		page_cache_release(pg);
 		return -ENOMEM;
 	}
 
@@ -287,11 +311,14 @@ static int jffs2_commit_write (struct fi
 		/* generic_file_write has written more to the page cache than we've
 		   actually written to the medium. Mark the page !Uptodate so that
 		   it gets reread */
-		D1(printk(KERN_DEBUG "jffs2_commit_write(): Not all bytes written. Marking page !uptodate\n"));
+		D1(printk(KERN_DEBUG "jffs2_write_end(): Not all bytes written. Marking page !uptodate\n"));
 		SetPageError(pg);
 		ClearPageUptodate(pg);
 	}
 
-	D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",start+writtenlen==end?0:ret));
-	return start+writtenlen==end?0:ret;
+	D1(printk(KERN_DEBUG "jffs2_write_end() returning %d\n",
+					writtenlen > 0 ? writtenlen : ret));
+	unlock_page(pg);
+	page_cache_release(pg);
+	return writtenlen > 0 ? writtenlen : ret;
 }
#49 lore raw

PATCH 37/41 ufs convert to new aops.

In reply to [email protected]
 fs/ufs/dir.c   |   56 +++++++++++++++++++++++++++++++++++++-------------------
 fs/ufs/inode.c |   23 +++++++++++++++++++----
 2 files changed, 56 insertions(+), 23 deletions(-)
fs/ufs/inode.c +19 -4
Index: linux-2.6/fs/ufs/inode.c
===================================================================
@@ -558,24 +558,39 @@ static int ufs_writepage(struct page *pa
 {
 	return block_write_full_page(page,ufs_getfrag_block,wbc);
 }
+
 static int ufs_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,ufs_getfrag_block);
 }
-static int ufs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+
+int __ufs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page,from,to,ufs_getfrag_block);
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				ufs_getfrag_block);
 }
+
+static int ufs_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return __ufs_write_begin(file, mapping, pos, len, flags, pagep, fsdata);
+}
+
 static sector_t ufs_bmap(struct address_space *mapping, sector_t block)
 {
 	return generic_block_bmap(mapping,block,ufs_getfrag_block);
 }
+
 const struct address_space_operations ufs_aops = {
 	.readpage = ufs_readpage,
 	.writepage = ufs_writepage,
 	.sync_page = block_sync_page,
-	.prepare_write = ufs_prepare_write,
-	.commit_write = generic_commit_write,
+	.write_begin = ufs_write_begin,
+	.write_end = generic_write_end,
 	.bmap = ufs_bmap
 };
 
fs/ufs/dir.c +37 -19

141 lines not on this page · raw message

#50 lore raw

PATCH 38/41 udf convert to new aops.

In reply to [email protected]

Convert udf to new aops. Also seem to have fixed pagecache corruption in udf_adinicb_commit_write -- page was marked uptodate when it is not. Also, fixed the silly setup where prepare_write was doing a kmap to be used in
commit_write: just do kmap_atomic in write_end. Use libfs helpers to make this easier.

 fs/udf/file.c  |   32 +++++++++++++-------------------
 fs/udf/inode.c |   11 +++++++----
 2 files changed, 20 insertions(+), 23 deletions(-)
fs/udf/file.c +13 -19
Index: linux-2.6/fs/udf/file.c
===================================================================
@@ -74,34 +74,28 @@ static int udf_adinicb_writepage(struct 
 	return 0;
 }
 
-static int udf_adinicb_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
+static int udf_adinicb_write_end(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned copied,
+			struct page *page, void *fsdata)
 {
-	kmap(page);
-	return 0;
-}
-
-static int udf_adinicb_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
-{
-	struct inode *inode = page->mapping->host;
-	char *kaddr = page_address(page);
+	struct inode *inode = mapping->host;
+	unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
+	char *kaddr;
 
+	kaddr = kmap_atomic(page, KM_USER0);
 	memcpy(UDF_I_DATA(inode) + UDF_I_LENEATTR(inode) + offset,
-		kaddr + offset, to - offset);
-	mark_inode_dirty(inode);
-	SetPageUptodate(page);
-	kunmap(page);
-	/* only one page here */
-	if (to > inode->i_size)
-		inode->i_size = to;
-	return 0;
+		kaddr + offset, copied);
+	kunmap_atomic(kaddr, KM_USER0);
+
+	return simple_write_end(file, mapping, pos, len, copied, page, fsdata);
 }
 
 const struct address_space_operations udf_adinicb_aops = {
 	.readpage		= udf_adinicb_readpage,
 	.writepage		= udf_adinicb_writepage,
 	.sync_page		= block_sync_page,
-	.prepare_write		= udf_adinicb_prepare_write,
-	.commit_write		= udf_adinicb_commit_write,
+	.write_begin		= simple_write_begin,
+	.write_end		= udf_adinicb_write_end,
 };
 
 static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
fs/udf/inode.c +7 -4
Index: linux-2.6/fs/udf/inode.c
===================================================================
@@ -123,9 +123,12 @@ static int udf_readpage(struct file *fil
 	return block_read_full_page(page, udf_get_block);
 }
 
-static int udf_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+static int udf_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page, from, to, udf_get_block);
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				udf_get_block);
 }
 
 static sector_t udf_bmap(struct address_space *mapping, sector_t block)
@@ -137,8 +140,8 @@ const struct address_space_operations ud
 	.readpage		= udf_readpage,
 	.writepage		= udf_writepage,
 	.sync_page		= block_sync_page,
-	.prepare_write		= udf_prepare_write,
-	.commit_write		= generic_commit_write,
+	.write_begin		= udf_write_begin,
+	.write_end		= generic_write_end,
 	.bmap			= udf_bmap,
 };
 
#51 lore raw

PATCH 39/41 sysv convert to new aops.

In reply to [email protected]
 fs/sysv/dir.c   |   51 +++++++++++++++++++++++++++++++--------------------
 fs/sysv/itree.c |   23 +++++++++++++++++++----
 2 files changed, 50 insertions(+), 24 deletions(-)
fs/sysv/itree.c +19 -4
Index: linux-2.6/fs/sysv/itree.c
===================================================================
@@ -453,23 +453,38 @@ static int sysv_writepage(struct page *p
 {
 	return block_write_full_page(page,get_block,wbc);
 }
+
 static int sysv_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,get_block);
 }
-static int sysv_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+
+int __sysv_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page,from,to,get_block);
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				get_block);
 }
+
+static int sysv_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return __sysv_write_begin(file, mapping, pos, len, flags, pagep, fsdata);
+}
+
 static sector_t sysv_bmap(struct address_space *mapping, sector_t block)
 {
 	return generic_block_bmap(mapping,block,get_block);
 }
+
 const struct address_space_operations sysv_aops = {
 	.readpage = sysv_readpage,
 	.writepage = sysv_writepage,
 	.sync_page = block_sync_page,
-	.prepare_write = sysv_prepare_write,
-	.commit_write = generic_commit_write,
+	.write_begin = sysv_write_begin,
+	.write_end = generic_write_end,
 	.bmap = sysv_bmap
 };
fs/sysv/dir.c +31 -20

128 lines not on this page · raw message

#52 lore raw

PATCH 40/41 minix convert to new aops.

In reply to [email protected]
Cc Andries Brouwer <[email protected]>
Cc Linux Filesystems <[email protected]>
 fs/minix/dir.c   |   50 ++++++++++++++++++++++++++++++++------------------
 fs/minix/inode.c |   23 +++++++++++++++++++----
 2 files changed, 51 insertions(+), 22 deletions(-)
fs/minix/inode.c +19 -4
Index: linux-2.6/fs/minix/inode.c
===================================================================
@@ -346,24 +346,39 @@ static int minix_writepage(struct page *
 {
 	return block_write_full_page(page, minix_get_block, wbc);
 }
+
 static int minix_readpage(struct file *file, struct page *page)
 {
 	return block_read_full_page(page,minix_get_block);
 }
-static int minix_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+
+int __minix_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
 {
-	return block_prepare_write(page,from,to,minix_get_block);
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				minix_get_block);
 }
+
+static int minix_write_begin(struct file *file, struct address_space *mapping,
+			loff_t pos, unsigned len, unsigned flags,
+			struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return __minix_write_begin(file, mapping, pos, len, flags, pagep, fsdata);
+}
+
 static sector_t minix_bmap(struct address_space *mapping, sector_t block)
 {
 	return generic_block_bmap(mapping,block,minix_get_block);
 }
+
 static const struct address_space_operations minix_aops = {
 	.readpage = minix_readpage,
 	.writepage = minix_writepage,
 	.sync_page = block_sync_page,
-	.prepare_write = minix_prepare_write,
-	.commit_write = generic_commit_write,
+	.write_begin = minix_write_begin,
+	.write_end = generic_write_end,
 	.bmap = minix_bmap
 };
 
fs/minix/dir.c +32 -18

117 lines not on this page · raw message

#53 lore raw

PATCH 41/41 jfs convert to new aops.

In reply to [email protected]
 fs/jfs/inode.c |   19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)
fs/jfs/inode.c +11 -8
Index: linux-2.6/fs/jfs/inode.c
===================================================================
@@ -255,7 +255,7 @@ int jfs_get_block(struct inode *ip, sect
 
 static int jfs_writepage(struct page *page, struct writeback_control *wbc)
 {
-	return nobh_writepage(page, jfs_get_block, wbc);
+	return block_write_full_page(page, jfs_get_block, wbc);
 }
 
 static int jfs_writepages(struct address_space *mapping,
@@ -275,10 +275,13 @@ static int jfs_readpages(struct file *fi
 	return mpage_readpages(mapping, pages, nr_pages, jfs_get_block);
 }
 
-static int jfs_prepare_write(struct file *file,
-			     struct page *page, unsigned from, unsigned to)
-{
-	return nobh_prepare_write(page, from, to, jfs_get_block);
+static int jfs_write_begin(struct file *file, struct address_space *mapping,
+				loff_t pos, unsigned len, unsigned flags,
+				struct page **pagep, void **fsdata)
+{
+	*pagep = NULL;
+	return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
+				jfs_get_block);
 }
 
 static sector_t jfs_bmap(struct address_space *mapping, sector_t block)
@@ -302,8 +305,8 @@ const struct address_space_operations jf
 	.writepage	= jfs_writepage,
 	.writepages	= jfs_writepages,
 	.sync_page	= block_sync_page,
-	.prepare_write	= jfs_prepare_write,
-	.commit_write	= nobh_commit_write,
+	.write_begin	= jfs_write_begin,
+	.write_end	= generic_write_end,
 	.bmap		= jfs_bmap,
 	.direct_IO	= jfs_direct_IO,
 };
@@ -356,7 +359,7 @@ void jfs_truncate(struct inode *ip)
 {
 	jfs_info("jfs_truncate: size = 0x%lx", (ulong) ip->i_size);
 
-	nobh_truncate_page(ip->i_mapping, ip->i_size);
+	block_truncate_page(ip->i_mapping, ip->i_size, jfs_get_block);
 
 	IWRITE_LOCK(ip, RDWRLOCK_NORMAL);
 	jfs_truncate_nolock(ip, ip->i_size);
Signature · 5 lines
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
#54 lore raw
In reply to [email protected]
Quoted 3 lines · On Fri, May 25, 2007 at 10:21:44PM +1000, [email protected] wrote:

Still unfortunately missing the OCFS2 and GFS2 conversions, which allowed us to remove a lot of code -- I won't ask the maintainers to redo them either until the patchset gets somewhere.

Nonetheless, I'll give this a go and try to give you an ocfs2 patch sometime next week. It should be much easier this time anyway. It turns out that the write_begin()/write_end() style interface works very well for ocfs2 internally, so I went back and merged most of that work into ocfs2.git for some of my shared writeable mmap work anyway.

	--Mark

--
Mark Fasheh
Senior Software Developer, Oracle
[email protected]