[2.0.30] ext2 write cleanup/speedup

Gordon Oliver (gordo@telsur.cl)
Wed, 6 Aug 1997 10:15:33 -0400 (CST)


Hi all.

This patch cleans up the code int ext2_write. Under heavy use, the
time spent in that function is reduced by about 5%, and besides, it
is easier to read. The patch does _not_ change any behaviour. It
moves a whole parcel of checks outside of the loop to the beginning
of the function.

one interesting note... The check for EFBIG is only triggered if a
seek is done, as ext2_get_blk fails at size = 0x7ffffc00 (2G - block
size I presume)

This code has been running on my system without problems for about 3
months...
--------------------------------------------------------------------

diff --recursive -u --new-file linux-2.0.30/fs/ext2/file.c work/fs/ext2/file.c
--- linux-2.0.30/fs/ext2/file.c Tue Feb 20 05:28:13 1996
+++ work/fs/ext2/file.c Mon Apr 28 14:25:39 1997
@@ -80,42 +80,63 @@
NULL /* smap */
};

+static int ext2_sync_write(int buffercount, struct buffer_head *bufferlist[])
+{
+ int i;
+ int write_error;
+
+ write_error = 0;
+ ll_rw_block(WRITE, buffercount, bufferlist);
+ wait_on_buffer(bufferlist[buffercount-1]);
+ for(i=0; i<buffercount; i++){
+ wait_on_buffer(bufferlist[i]);
+ if (!buffer_uptodate(bufferlist[i]))
+ write_error=1;
+ brelse(bufferlist[i]);
+ }
+ return write_error;
+}
+
+/*
+ * known bugs (from looking at the code)
+ * 1) count is treated as signed, and should be unsigned.
+ * 2) disk write error results in incorrect size passed back on
+ * O_SYNC files.
+ * 3) inodes are not written back at the end of the O_SYNC write.
+ */
static int ext2_file_write (struct inode * inode, struct file * filp,
const char * buf, int count)
{
const loff_t two_gb = 2147483647;
loff_t pos;
- off_t pos2;
long block;
int offset;
int written, c;
struct buffer_head * bh, *bufferlist[NBUF];
struct super_block * sb;
int err;
- int i,buffercount,write_error;
+ int buffercount;

- write_error = buffercount = 0;
- if (!inode) {
- printk("ext2_file_write: inode = NULL\n");
- return -EINVAL;
- }
+ buffercount = 0;
+ written = 0;
+ if (!inode)
+ goto bad_inode;
sb = inode->i_sb;
if (sb->s_flags & MS_RDONLY)
- /*
- * This fs has been automatically remounted ro because of errors
- */
- return -ENOSPC;
-
- if (!S_ISREG(inode->i_mode)) {
- ext2_warning (sb, "ext2_file_write", "mode = %07o",
- inode->i_mode);
- return -EINVAL;
- }
+ goto ro_fs;
+
+ if (!S_ISREG(inode->i_mode))
+ goto bad_mode;
if (filp->f_flags & O_APPEND)
pos = inode->i_size;
else
pos = filp->f_pos;
- pos2 = (off_t) pos;
+ if (count <= 0) /* broken? */
+ goto exit;
+ if (pos >= two_gb)
+ goto big_file;
+ if (two_gb - pos < count)
+ count = two_gb - pos;
/*
* If a file has been opened in synchronous mode, we have to ensure
* that meta-data will also be written synchronously. Thus, we
@@ -124,22 +145,13 @@
*/
if (filp->f_flags & O_SYNC)
inode->u.ext2_i.i_osync++;
- block = pos2 >> EXT2_BLOCK_SIZE_BITS(sb);
- offset = pos2 & (sb->s_blocksize - 1);
+ block = ((off_t)pos) >> EXT2_BLOCK_SIZE_BITS(sb);
+ offset = ((off_t)pos) & (sb->s_blocksize - 1);
c = sb->s_blocksize - offset;
- written = 0;
while (count > 0) {
- if (pos > two_gb) {
- if (!written)
- written = -EFBIG;
- break;
- }
bh = ext2_getblk (inode, block, 1, &err);
- if (!bh) {
- if (!written)
- written = err;
- break;
- }
+ if (!bh)
+ goto no_block;
count -= c;
if (count < 0)
c += count;
@@ -155,49 +167,66 @@
}
memcpy_fromfs (bh->b_data + offset, buf, c);
update_vm_cache(inode, pos, bh->b_data + offset, c);
- pos2 += c;
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 0);
pos += c;
written += c;
buf += c;
- mark_buffer_uptodate(bh, 1);
- mark_buffer_dirty(bh, 0);
- if (filp->f_flags & O_SYNC)
- bufferlist[buffercount++] = bh;
- else
- brelse(bh);
- if (buffercount == NBUF){
- ll_rw_block(WRITE, buffercount, bufferlist);
- for(i=0; i<buffercount; i++){
- wait_on_buffer(bufferlist[i]);
- if (!buffer_uptodate(bufferlist[i]))
- write_error=1;
- brelse(bufferlist[i]);
- }
- buffercount=0;
- }
- if(write_error)
- break;
block++;
offset = 0;
c = sb->s_blocksize;
- }
- if ( buffercount ){
- ll_rw_block(WRITE, buffercount, bufferlist);
- for(i=0; i<buffercount; i++){
- wait_on_buffer(bufferlist[i]);
- if (!buffer_uptodate(bufferlist[i]))
- write_error=1;
- brelse(bufferlist[i]);
+ if (!(filp->f_flags & O_SYNC)) {
+ brelse(bh);
+ continue;
}
- }
- if (pos > inode->i_size)
- inode->i_size = pos;
+ else {
+ bufferlist[buffercount++] = bh;
+ if (buffercount == NBUF) {
+ int tmp = buffercount;
+
+ buffercount = 0;
+ if (ext2_sync_write(tmp, bufferlist))
+ break;
+ }
+ }
+ }
+clean_and_exit:
if (filp->f_flags & O_SYNC)
+ {
+ if ( buffercount )
+ ext2_sync_write(buffercount, bufferlist);
inode->u.ext2_i.i_osync--;
+ }
+ if (pos > inode->i_size)
+ inode->i_size = pos;
inode->i_ctime = inode->i_mtime = CURRENT_TIME;
filp->f_pos = pos;
inode->i_dirt = 1;
+exit:
return written;
+
+no_block:
+ if (!written)
+ written = err;
+ goto clean_and_exit;
+
+bad_inode:
+ printk("ext2_file_write: inode = NULL\n");
+ return -EINVAL;
+
+bad_mode:
+ ext2_warning (sb, "ext2_file_write", "mode = %07o",
+ inode->i_mode);
+ return -EINVAL;
+
+big_file:
+ return -EFBIG;
+
+ro_fs:
+ /*
+ * This fs has been automatically remounted ro because of errors
+ */
+ return -ENOSPC;
}

/*