Re: Still ext2-corruption in test8-pre5 (incl. OOPS)

From: Linus Torvalds (torvalds@transmeta.com)
Date: Tue Sep 05 2000 - 21:14:02 EST


How about this patch?

NOTE NOTE NOTE! I'm on my way home now to be a family man, so I've not
actually tested it AT ALL. You have been warned.

The basic approach should be ok, and it avoids using the write path at all
because it isn't actually needed. The truncate() case is, in the end, much
simpler than writing, exactly because we don't need to allocate any new
blocks etc.

We just grab the page, populate it with buffers if required, and find the
one buffer that we need to clear out. We clear it out and mark it dirty.
End of story.

NOTE: Udo, because I haven't actually tested this (it may not actually
compile etc small details), you probably shouldn't actually test this out
as-is unless you are _really_ daring and don't mind fixing up after me.
It's more a "this is how it should work" kind of thing.

Al? Mind giving it a quick look?

                Linus

---
diff -u --recursive --new-file p5/linux/fs/buffer.c linux/fs/buffer.c
--- p5/linux/fs/buffer.c	Tue Sep  5 18:56:06 2000
+++ linux/fs/buffer.c	Tue Sep  5 19:09:08 2000
@@ -1724,31 +1724,63 @@
 	return 0;
 }
 
-int block_zero_page(struct address_space *mapping, loff_t from, unsigned length)
+int block_truncate_page(struct address_space *mapping, loff_t from, get_block_t *get_block)
 {
 	unsigned long index = from >> PAGE_CACHE_SHIFT;
 	unsigned offset = from & (PAGE_CACHE_SIZE-1);
+	unsigned blocksize, iblock, length, pos;
 	struct inode *inode = (struct inode *)mapping->host;
 	struct page *page;
+	struct buffer_head *bh;
 	int err;
 
+	blocksize = inode->i_sb->s_blocksize;
+	length = offset & (blocksize - 1);
+
+	/* Block boundary? Nothing to do */
 	if (!length)
 		return 0;
 
-	page = read_cache_page(mapping, index,
-				(filler_t *)mapping->a_ops->readpage, NULL);
+	length = blocksize - length;
+	iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
+	
+	page = grab_cache_page(mapping, index);
 	err = PTR_ERR(page);
 	if (IS_ERR(page))
 		goto out;
-	lock_page(page);
-	err = -EIO;
-	if (!Page_Uptodate(page))
-		goto unlock;
+
+	if (!page->buffers)
+		create_empty_buffers(page, inode, blocksize);
+
+	/* Find the buffer that contains "offset" */
+	bh = page->buffers;
+	pos = blocksize;
+	while (offset >= pos) {
+		bh = bh->b_next;
+		iblock++;
+		pos += blocksize;
+	}
+
+	if (!buffer_uptodate(bh)) {
+		err = 0;
+		if (!buffer_mapped(bh)) {
+			get_block(inode, iblock, bh, 0);
+			if (!buffer_mapped(bh))
+				goto unlock;
+		}
+		err = -EIO;
+		bh->b_end_io = end_buffer_io_sync;
+		ll_rw_block(READ, 1, &bh);
+		wait_on_buffer(bh);
+		if (!buffer_uptodate(bh))
+			goto unlock;
+	}
 
 	memset((char *) kmap(page) + offset, 0, length);
 	flush_dcache_page(page);
-	__block_commit_write(inode, page, offset, offset+length);
 	kunmap(page);
+
+	mark_buffer_dirty(bh);
 	err = 0;
 
 unlock:
diff -u --recursive --new-file p5/linux/fs/ext2/inode.c linux/fs/ext2/inode.c
--- p5/linux/fs/ext2/inode.c	Tue Sep  5 18:56:06 2000
+++ linux/fs/ext2/inode.c	Tue Sep  5 19:01:29 2000
@@ -874,7 +874,7 @@
 	int nr = 0;
 	int n;
 	long iblock;
-	unsigned blocksize, tail;
+	unsigned blocksize;
 
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 	    S_ISLNK(inode->i_mode)))
@@ -887,9 +887,8 @@
 	blocksize = inode->i_sb->s_blocksize;
 	iblock = (inode->i_size + blocksize-1)
 					>> EXT2_BLOCK_SIZE_BITS(inode->i_sb);
-	tail = (iblock << EXT2_BLOCK_SIZE_BITS(inode->i_sb)) - inode->i_size;
 
-	block_zero_page(inode->i_mapping, inode->i_size, tail);
+	block_truncate_page(inode->i_mapping, inode->i_size, ext2_get_block);
 
 	n = ext2_block_to_path(inode, iblock, offsets);
 	if (n == 0)
diff -u --recursive --new-file p5/linux/include/linux/fs.h linux/include/linux/fs.h
--- p5/linux/include/linux/fs.h	Tue Sep  5 18:56:07 2000
+++ linux/include/linux/fs.h	Tue Sep  5 19:00:59 2000
@@ -1173,7 +1173,7 @@
 
 int generic_block_bmap(struct address_space *, long, get_block_t *);
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
-int block_zero_page(struct address_space *, loff_t, unsigned);
+int block_truncate_page(struct address_space *, loff_t, get_block_t *);
 
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
diff -u --recursive --new-file p5/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- p5/linux/kernel/ksyms.c	Tue Sep  5 18:56:07 2000
+++ linux/kernel/ksyms.c	Tue Sep  5 18:56:47 2000
@@ -203,7 +203,7 @@
 EXPORT_SYMBOL(block_sync_page);
 EXPORT_SYMBOL(cont_prepare_write);
 EXPORT_SYMBOL(generic_commit_write);
-EXPORT_SYMBOL(block_zero_page);
+EXPORT_SYMBOL(block_truncate_page);
 EXPORT_SYMBOL(generic_block_bmap);
 EXPORT_SYMBOL(generic_file_read);
 EXPORT_SYMBOL(do_generic_file_read);

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Thu Sep 07 2000 - 21:00:24 EST