--- drivers/acorn/block/fd1772.c.~1~ Thu Jan 7 16:46:58 1999 +++ drivers/acorn/block/fd1772.c Thu May 27 23:57:19 1999 @@ -1569,7 +1569,7 @@ if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT))) /* if the file is mounted OR (writable now AND writable at open time) Linus: Does this cover all cases? */ - block_fsync(inode, filp); + block_fsync(inode, filp, 0); if (fd_ref[drive] < 0) fd_ref[drive] = 0; --- drivers/block/ataflop.c.~1~ Tue Dec 29 19:23:59 1998 +++ drivers/block/ataflop.c Thu May 27 23:57:20 1999 @@ -1986,7 +1986,7 @@ * sync is required. Otherwise, sync if filp is writable. */ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT))) - block_fsync (filp, filp->f_dentry); + block_fsync (filp, filp->f_dentry, 0); if (fd_ref[drive] < 0) fd_ref[drive] = 0; --- drivers/block/floppy.c.~1~ Tue Apr 20 22:33:21 1999 +++ drivers/block/floppy.c Thu May 27 23:57:20 1999 @@ -3677,7 +3677,7 @@ * sync is required. Otherwise, sync if filp is writable. */ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT))) - block_fsync(filp, filp->f_dentry); + block_fsync(filp, filp->f_dentry, 0); if (UDRS->fd_ref < 0) UDRS->fd_ref=0; --- drivers/block/swim3.c.~1~ Tue Apr 20 22:33:02 1999 +++ drivers/block/swim3.c Thu May 27 23:57:20 1999 @@ -941,7 +941,7 @@ * sync is required. Otherwise, sync if filp is writable. */ if (filp && (filp->f_mode & (2 | OPEN_WRITE_BIT))) - block_fsync (filp, filp->f_dentry); + block_fsync (filp, filp->f_dentry, 0); fs = &floppy_states[devnum]; sw = fs->swim3; --- fs/block_dev.c.~1~ Fri Nov 13 18:07:26 1998 +++ fs/block_dev.c Thu May 27 23:57:20 1999 @@ -292,7 +292,7 @@ * since the vma has no handle. */ -int block_fsync(struct file *filp, struct dentry *dentry) +int block_fsync(struct file *filp, struct dentry *dentry, int datasync) { return fsync_dev(dentry->d_inode->i_rdev); } --- fs/buffer.c.~1~ Thu May 27 23:54:35 1999 +++ fs/buffer.c Fri May 28 00:27:04 1999 @@ -55,6 +55,8 @@ #define MAX_UNUSED_BUFFERS NR_RESERVED+20 /* don't ever have more than this number of unused buffer heads */ +#define BH_ENTRY(list) list_entry((list), struct buffer_head, b_inode_buffers) + /* * Hash table mask.. */ @@ -301,7 +303,7 @@ * filp may be NULL if called via the msync of a vma. */ -int file_fsync(struct file *filp, struct dentry *dentry) +int file_fsync(struct file *filp, struct dentry *dentry, int datasync) { struct inode * inode = dentry->d_inode; struct super_block * sb; @@ -348,7 +350,7 @@ /* We need to protect against concurrent writers.. */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry); + err = file->f_op->fsync(file, dentry, 0); up(&inode->i_sem); out_putf: @@ -383,9 +385,8 @@ if (!file->f_op || !file->f_op->fsync) goto out_putf; - /* this needs further work, at the moment it is identical to fsync() */ down(&inode->i_sem); - err = file->f_op->fsync(file, dentry); + err = file->f_op->fsync(file, dentry, 1); up(&inode->i_sem); out_putf: @@ -547,6 +548,141 @@ } } +void buffer_insert_inode_queue(struct buffer_head *bh, struct inode *inode) +{ + if (bh->b_inode) + list_del(&bh->b_inode_buffers); + bh->b_inode = inode; + list_add(&bh->b_inode_buffers, &inode->i_dirty_buffers); +} + +static void __remove_inode_queue(struct buffer_head *bh) +{ + bh->b_inode = NULL; + list_del(&bh->b_inode_buffers); +} + +static inline void remove_inode_queue(struct buffer_head *bh) +{ + if (bh->b_inode) + __remove_inode_queue(bh); +} + + +/* + * Synchronise all the inode's dirty buffers to the disk. + * + * We have conflicting pressures: we want to make sure that all + * initially dirty buffers get waited on, but that any subsequently + * dirtied buffers don't. After all, we don't want fsync to last + * forever if somebody is actively writing to the file. + * + * Do this in two main stages: first we copy dirty buffers to a + * temporary inode list, queueing the writes as we go. Then we clean + * up, waiting for those writes to complete. + * + * During this second stage, any subsequent updates to the file may end + * up refiling the buffer on the original inode's dirty list again, so + * there is a chance we will end up with a buffer queued for write but + * not yet completed on that list. So, as a final cleanup we go through + * the osync code to catch these locked, dirty buffers without requeuing + * any newly dirty buffers for write. + */ + +int fsync_inode_buffers(struct inode *inode) +{ + struct buffer_head *bh; + struct inode tmp; + int err = 0, err2; + + INIT_LIST_HEAD(&tmp.i_dirty_buffers); + + while (!list_empty(&inode->i_dirty_buffers)) { + bh = BH_ENTRY(inode->i_dirty_buffers.next); + list_del(&bh->b_inode_buffers); + if (!buffer_dirty(bh) && !buffer_locked(bh)) + bh->b_inode = NULL; + else { + bh->b_inode = &tmp; + list_add(&bh->b_inode_buffers, &tmp.i_dirty_buffers); + bh->b_count++; + if (buffer_dirty(bh)) + ll_rw_block(WRITE, 1, &bh); + } + } + + while (!list_empty(&tmp.i_dirty_buffers)) { + bh = BH_ENTRY(tmp.i_dirty_buffers.prev); + remove_inode_queue(bh); + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + err = -EIO; + brelse(bh); + } + + err2 = osync_inode_buffers(inode); + + if (err) + return err; + else + return err2; +} + + +/* + * osync is designed to support O_SYNC io. It waits synchronously for + * all already-submitted IO to complete, but does not queue any new + * writes to the disk. + * + * To do O_SYNC writes, just queue the buffer writes with ll_rw_block as + * you dirty the buffers, and then use osync_inode_buffers to wait for + * completion. Any other dirty buffers which are not yet queued for + * write will not be flushed to disk by the osync. + */ + +int osync_inode_buffers(struct inode *inode) +{ + struct buffer_head *bh; + struct list_head *list; + int err = 0; + + repeat: + + for (list = inode->i_dirty_buffers.prev; + bh = BH_ENTRY(list), list != &inode->i_dirty_buffers; + list = bh->b_inode_buffers.prev) { + if (buffer_locked(bh)) { + bh->b_count++; + wait_on_buffer(bh); + brelse(bh); + if (!buffer_uptodate(bh)) + err = -EIO; + goto repeat; + } + } + return err; +} + + +/* + * Invalidate any and all dirty buffers on a given inode. We are + * probably unmounting the fs, but that doesn't mean we have already + * done a sync(). Just drop the buffers from the inode list. + */ + +void invalidate_inode_buffers(struct inode *inode) +{ + struct list_head *list, *next; + + list = inode->i_dirty_buffers.next; + while (list != &inode->i_dirty_buffers) { + next = list->next; + remove_inode_queue(BH_ENTRY(list)); + list = next; + } +} + + struct buffer_head * find_buffer(kdev_t dev, int block, int size) { struct buffer_head * next; @@ -557,7 +693,7 @@ if (!next) break; next = tmp->b_next; - if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev) + if (tmp->b_blocknr != block || tmp->b_dev != dev || tmp->b_size != size) continue; next = tmp; break; @@ -570,8 +706,7 @@ * As we don't lock buffers (unless we are reading them, that is), * something might happen to it while we sleep (ie a read-error * will force it bad). This shouldn't really happen currently, but - * the code is ready. - */ + * the code is ready. */ struct buffer_head * get_hash_table(kdev_t dev, int block, int size) { struct buffer_head * bh; @@ -776,13 +911,14 @@ dispose = BUF_CLEAN; if(dispose != buf->b_list) { file_buffer(buf, dispose); - if(dispose == BUF_DIRTY) { + switch (dispose) { + case BUF_DIRTY: { int too_many = (nr_buffers * bdf_prm.b_un.nfract/100); /* This buffer is dirty, maybe we need to start flushing. * If too high a percentage of the buffers are dirty... */ - if (nr_buffers_type[BUF_DIRTY] > too_many) + if (nr_buffers_type[BUF_DIRTY] > too_many) wakeup_bdflush(1); /* If this is a loop device, and @@ -792,6 +928,11 @@ if (MAJOR(buf->b_dev) == LOOP_MAJOR && nr_buffers_type[BUF_DIRTY]*2>nr_buffers) wakeup_bdflush(1); + break; + } + case BUF_CLEAN: + remove_inode_queue(buf); + default: } } } @@ -821,6 +962,7 @@ */ void __bforget(struct buffer_head * buf) { + remove_inode_queue(buf); if (buf->b_count != 1 || buffer_locked(buf)) { __brelse(buf); return; @@ -1436,6 +1578,7 @@ tmp = tmp->b_this_page; nr_buffers--; remove_from_queues(p); + remove_inode_queue(p); put_unused_buffer_head(p); } while (tmp != bh); --- fs/coda/dir.c.~1~ Wed May 12 15:54:22 1999 +++ fs/coda/dir.c Thu May 27 23:57:20 1999 @@ -50,7 +50,7 @@ /* support routines */ static int coda_venus_readdir(struct file *filp, void *dirent, filldir_t filldir); -int coda_fsync(struct file *, struct dentry *dentry); +int coda_fsync(struct file *, struct dentry *dentry, int); int coda_crossvol_rename = 0; int coda_hasmknod = 0; --- fs/coda/file.c.~1~ Mon Aug 31 23:46:10 1998 +++ fs/coda/file.c Thu May 27 23:57:20 1999 @@ -32,7 +32,7 @@ static int coda_file_mmap(struct file * file, struct vm_area_struct * vma); /* also exported from this file (used for dirs) */ -int coda_fsync(struct file *, struct dentry *dentry); +int coda_fsync(struct file *, struct dentry *dentry, int); struct inode_operations coda_file_inode_operations = { &coda_file_operations, /* default file operations */ @@ -205,7 +205,7 @@ return result; } -int coda_fsync(struct file *coda_file, struct dentry *coda_dentry) +int coda_fsync(struct file *coda_file, struct dentry *coda_dentry, int datasync) { struct coda_inode_info *cnp; struct inode *coda_inode = coda_dentry->d_inode; @@ -234,7 +234,7 @@ down(&cont_inode->i_sem); - result = file_fsync(&cont_file ,&cont_dentry); + result = file_fsync(&cont_file ,&cont_dentry, 0); if ( result == 0 ) { result = venus_fsync(coda_inode->i_sb, &(cnp->c_fid)); } --- fs/ext2/dir.c.~1~ Wed May 12 15:53:58 1999 +++ fs/ext2/dir.c Thu May 27 23:57:20 1999 @@ -45,7 +45,7 @@ NULL, /* no special open code */ NULL, /* flush */ NULL, /* no special release code */ - ext2_sync_file, /* fsync */ + ext2_fsync_file, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ --- fs/ext2/file.c.~1~ Mon Dec 21 23:22:54 1998 +++ fs/ext2/file.c Thu May 27 23:57:20 1999 @@ -76,7 +76,7 @@ #endif NULL, /* flush */ ext2_release_file, /* release */ - ext2_sync_file, /* fsync */ + ext2_fsync_file, /* fsync */ NULL, /* fasync */ NULL, /* check_media_change */ NULL /* revalidate */ @@ -270,7 +270,10 @@ buf += c; count -= c; mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 0); + /* Even if the file is open for O_SYNC write, we need to + * attach the dirty buffer to the inode to avoid a + * concurrent fsync() returning early. */ + mark_buffer_dirty_inode(bh, 0, inode); if (filp->f_flags & O_SYNC) bufferlist[buffercount++] = bh; @@ -279,7 +282,6 @@ if (buffercount == NBUF){ ll_rw_block(WRITE, buffercount, bufferlist); for(i=0; iu.ext2_i.i_osync--; inode->i_ctime = inode->i_mtime = CURRENT_TIME; *ppos = pos; - mark_inode_dirty(inode); - return written; + mark_inode_dirty_sync(inode); + if (filp->f_flags & O_SYNC) + write_error |= ext2_osync_inode(inode, 1); + if (written) + return written; + if (write_error) + return -EIO; + return 0; } /* --- fs/ext2/fsync.c.~1~ Wed May 20 21:09:12 1998 +++ fs/ext2/fsync.c Thu May 27 23:57:20 1999 @@ -32,254 +32,49 @@ #include -#define blocksize (EXT2_BLOCK_SIZE(inode->i_sb)) -#define addr_per_block (EXT2_ADDR_PER_BLOCK(inode->i_sb)) -static int sync_block (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!*block) - return 0; - bh = get_hash_table (inode->i_dev, *block, blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_block_swab32 (struct inode * inode, u32 * block, int wait) -{ - struct buffer_head * bh; - - if (!le32_to_cpu(*block)) - return 0; - bh = get_hash_table (inode->i_dev, le32_to_cpu(*block), blocksize); - if (!bh) - return 0; - if (wait && buffer_req(bh) && !buffer_uptodate(bh)) { - brelse (bh); - return -1; - } - if (wait || !buffer_uptodate(bh) || !buffer_dirty(bh)) { - brelse (bh); - return 0; - } - ll_rw_block (WRITE, 1, &bh); - bh->b_count--; - return 0; -} -#else -#define sync_block_swab32 sync_block -#endif - - -static int sync_iblock (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; - tmp = *iblock; - if (!tmp) - return 0; - rc = sync_block (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} - -#ifndef __LITTLE_ENDIAN -static int sync_iblock_swab32 (struct inode * inode, u32 * iblock, - struct buffer_head ** bh, int wait) -{ - int rc, tmp; - - *bh = NULL; - tmp = le32_to_cpu(*iblock); - if (!tmp) - return 0; - rc = sync_block_swab32 (inode, iblock, wait); - if (rc) - return rc; - *bh = bread (inode->i_dev, tmp, blocksize); - if (!*bh) - return -1; - return 0; -} -#else -#define sync_iblock_swab32 sync_iblock -#endif +/* + * File may be NULL when we are called. Perhaps we shouldn't + * even pass file to fsync ? + */ -static int sync_direct (struct inode * inode, int wait) +int ext2_fsync_file(struct file * file, struct dentry *dentry, int datasync) { - int i; - int rc, err = 0; - - for (i = 0; i < EXT2_NDIR_BLOCKS; i++) { - rc = sync_block (inode, inode->u.ext2_i.i_data + i, wait); - if (rc) - err = rc; - } - return err; + struct inode *inode = dentry->d_inode; + return ext2_fsync_inode(inode, datasync); } -static int sync_indirect (struct inode * inode, u32 * iblock, int wait) +int ext2_fsync_inode(struct inode *inode, int datasync) { - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; + int err; - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; -} - -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_indirect_swab32 (struct inode * inode, u32 * iblock, int wait) -{ - int i; - struct buffer_head * ind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, iblock, &ind_bh, wait); - if (rc || !ind_bh) - return rc; + err = fsync_inode_buffers(inode); + if (!(inode->i_state & I_DIRTY)) + return err; + if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) + return err; - for (i = 0; i < addr_per_block; i++) { - rc = sync_block_swab32 (inode, - ((u32 *) ind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (ind_bh); - return err; + err |= ext2_sync_inode(inode); + return err ? -EIO : 0; } -#else -#define sync_indirect_swab32 sync_indirect -#endif -static int sync_dindirect (struct inode * inode, u32 * diblock, int wait) -{ - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; - - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} +/* + * Unlike the fsync functions, ext2_osync_inode only waits for + * completion of already-scheduled writes on this inode. It does not + * write out any new dirty data. + */ -#ifndef __LITTLE_ENDIAN -static __inline__ int sync_dindirect_swab32 (struct inode * inode, u32 * diblock, int wait) +int ext2_osync_inode(struct inode *inode, int datasync) { - int i; - struct buffer_head * dind_bh; - int rc, err = 0; - - rc = sync_iblock_swab32 (inode, diblock, &dind_bh, wait); - if (rc || !dind_bh) - return rc; + int err; - for (i = 0; i < addr_per_block; i++) { - rc = sync_indirect_swab32 (inode, - ((u32 *) dind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (dind_bh); - return err; -} -#else -#define sync_dindirect_swab32 sync_dindirect -#endif - -static int sync_tindirect (struct inode * inode, u32 * tiblock, int wait) -{ - int i; - struct buffer_head * tind_bh; - int rc, err = 0; - - rc = sync_iblock (inode, tiblock, &tind_bh, wait); - if (rc || !tind_bh) - return rc; + err = osync_inode_buffers(inode); + if (!(inode->i_state & I_DIRTY)) + return err; + if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) + return err; - for (i = 0; i < addr_per_block; i++) { - rc = sync_dindirect_swab32 (inode, - ((u32 *) tind_bh->b_data) + i, - wait); - if (rc) - err = rc; - } - brelse (tind_bh); - return err; -} - -/* - * File may be NULL when we are called. Perhaps we shouldn't - * even pass file to fsync ? - */ - -int ext2_sync_file(struct file * file, struct dentry *dentry) -{ - int wait, err = 0; - struct inode *inode = dentry->d_inode; - - if (S_ISLNK(inode->i_mode) && !(inode->i_blocks)) - /* - * Don't sync fast links! - */ - goto skip; - - for (wait=0; wait<=1; wait++) - { - err |= sync_direct (inode, wait); - err |= sync_indirect (inode, - inode->u.ext2_i.i_data+EXT2_IND_BLOCK, - wait); - err |= sync_dindirect (inode, - inode->u.ext2_i.i_data+EXT2_DIND_BLOCK, - wait); - err |= sync_tindirect (inode, - inode->u.ext2_i.i_data+EXT2_TIND_BLOCK, - wait); - } -skip: - err |= ext2_sync_inode (inode); + err |= ext2_sync_inode(inode); return err ? -EIO : 0; } + --- fs/ext2/inode.c.~1~ Wed May 12 15:54:22 1999 +++ fs/ext2/inode.c Thu May 27 23:57:20 1999 @@ -123,7 +123,7 @@ } memset(bh->b_data, 0, inode->i_sb->s_blocksize); mark_buffer_uptodate(bh, 1); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty_inode(bh, 1, inode); brelse (bh); } else { ext2_discard_prealloc (inode); @@ -270,10 +270,7 @@ inode->u.ext2_i.i_next_alloc_goal = tmp; inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) - ext2_sync_inode (inode); - else - mark_inode_dirty(inode); + mark_inode_dirty(inode); return result; } @@ -350,11 +347,9 @@ goto repeat; } *p = le32_to_cpu(tmp); - mark_buffer_dirty(bh, 1); - if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) { + mark_buffer_dirty_inode(bh, 1, inode); + if (IS_SYNC(inode) || inode->u.ext2_i.i_osync) ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } inode->i_ctime = CURRENT_TIME; inode->i_blocks += blocks; mark_inode_dirty(inode); --- fs/ext2/namei.c.~1~ Wed May 12 15:54:22 1999 +++ fs/ext2/namei.c Thu May 27 23:57:20 1999 @@ -302,7 +302,7 @@ dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty_inode(bh, 1, dir); *res_dir = de; *err = 0; return bh; @@ -384,14 +384,20 @@ EXT2_FEATURE_INCOMPAT_FILETYPE)) de->file_type = EXT2_FT_REG_FILE; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + + err = 0; + mark_buffer_dirty_inode(bh, 1, dir); if (IS_SYNC(dir)) { + if (ext2_osync_inode(inode, 1)) + err = -EIO; ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); + if (ext2_osync_inode(dir, 1)) + err = -EIO; } + brelse (bh); d_instantiate(dentry, inode); - return 0; + return err; } int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) @@ -437,14 +443,18 @@ if (S_ISBLK(mode) || S_ISCHR(mode)) inode->i_rdev = to_kdev_t(rdev); mark_inode_dirty(inode); - mark_buffer_dirty(bh, 1); + mark_buffer_dirty_inode(bh, 1, dir); + + err = 0; if (IS_SYNC(dir)) { + if (ext2_osync_inode(inode, 1)) + err = -EIO; ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); + if (ext2_osync_inode(dir, 1)) + err = -EIO; } d_instantiate(dentry, inode); brelse(bh); - err = 0; out: return err; @@ -498,7 +508,7 @@ EXT2_FEATURE_INCOMPAT_FILETYPE)) de->file_type = EXT2_FT_DIR; inode->i_nlink = 2; - mark_buffer_dirty(dir_block, 1); + mark_buffer_dirty_inode(dir_block, 1, dir); brelse (dir_block); inode->i_mode = S_IFDIR | (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask); if (dir->i_mode & S_ISGID) @@ -511,18 +521,23 @@ if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) de->file_type = EXT2_FT_DIR; + mark_buffer_dirty_inode(bh, 1, dir); dir->i_version = ++event; - mark_buffer_dirty(bh, 1); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } dir->i_nlink++; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); + + err = 0; + if (IS_SYNC(dir)) { + if (ext2_osync_inode(inode, 1)) + err = -EIO; + ll_rw_block (WRITE, 1, &bh); + if (ext2_osync_inode(dir, 1)) + err = -EIO; + } + d_instantiate(dentry, inode); brelse (bh); - err = 0; out: return err; @@ -622,11 +637,7 @@ dir->i_version = ++event; if (retval) goto end_rmdir; - mark_buffer_dirty(bh, 1); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } + mark_buffer_dirty_inode(bh, 1, dir); if (inode->i_nlink != 2) ext2_warning (inode->i_sb, "ext2_rmdir", "empty directory has nlink!=2 (%d)", @@ -639,6 +650,14 @@ inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); + retval = 0; + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + if (ext2_osync_inode(dir, 1)) + retval = -EIO; + if (ext2_osync_inode(inode, 1)) + retval = -EIO; + } d_delete(dentry); end_rmdir: @@ -675,11 +694,7 @@ if (retval) goto end_unlink; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } + mark_buffer_dirty_inode(bh, 1, dir); dir->i_ctime = dir->i_mtime = CURRENT_TIME; dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; mark_inode_dirty(dir); @@ -687,6 +702,13 @@ mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime; retval = 0; + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + if (ext2_osync_inode(dir, 1)) + retval = -EIO; + if (ext2_osync_inode(inode, 1)) + retval = -EIO; + } d_delete(dentry); /* This also frees the inode */ end_unlink: @@ -734,7 +756,7 @@ link[i++] = c; link[i] = 0; if (name_block) { - mark_buffer_dirty(name_block, 1); + mark_buffer_dirty_inode(name_block, 1, inode); brelse (name_block); } inode->i_size = i; @@ -748,14 +770,15 @@ EXT2_FEATURE_INCOMPAT_FILETYPE)) de->file_type = EXT2_FT_SYMLINK; dir->i_version = ++event; - mark_buffer_dirty(bh, 1); + mark_buffer_dirty_inode(bh, 1, dir); + err = 0; if (IS_SYNC(dir)) { ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); + if (ext2_osync_inode(dir, 1)) + err = -EIO; } brelse (bh); d_instantiate(dentry, inode); - err = 0; out: return err; @@ -801,18 +824,20 @@ de->file_type = EXT2_FT_FIFO; } dir->i_version = ++event; - mark_buffer_dirty(bh, 1); - if (IS_SYNC(dir)) { - ll_rw_block (WRITE, 1, &bh); - wait_on_buffer (bh); - } - brelse (bh); + mark_buffer_dirty_inode(bh, 1, dir); inode->i_nlink++; inode->i_ctime = CURRENT_TIME; mark_inode_dirty(inode); inode->i_count++; + err = 0; + if (IS_SYNC(dir)) { + ll_rw_block (WRITE, 1, &bh); + if (ext2_osync_inode(dir, 1)) + err = -EIO; + } + brelse (bh); d_instantiate(dentry, inode); - return 0; + return err; } #define PARENT_INO(buffer) \ @@ -902,7 +927,7 @@ mark_inode_dirty(old_dir); if (dir_bh) { PARENT_INO(dir_bh->b_data) = le32_to_cpu(new_dir->i_ino); - mark_buffer_dirty(dir_bh, 1); + mark_buffer_dirty_inode(dir_bh, 1, old_inode); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { @@ -914,19 +939,32 @@ mark_inode_dirty(new_dir); } } - mark_buffer_dirty(old_bh, 1); - if (IS_SYNC(old_dir)) { - ll_rw_block (WRITE, 1, &old_bh); - wait_on_buffer (old_bh); - } - mark_buffer_dirty(new_bh, 1); + + retval = 0; + + /* We do sync processing on the new directory first to ensure + * that on a crash, we never actually loose the dirent */ + mark_buffer_dirty_inode(new_bh, 1, new_dir); if (IS_SYNC(new_dir)) { ll_rw_block (WRITE, 1, &new_bh); - wait_on_buffer (new_bh); + if (ext2_osync_inode(new_dir, 1)) + retval = -EIO; } - retval = 0; + mark_buffer_dirty_inode(old_bh, 1, old_dir); + if (IS_SYNC(old_dir)) { + ll_rw_block (WRITE, 1, &old_bh); + if (ext2_osync_inode(old_dir, 1)) + retval = -EIO; + } + if (IS_SYNC(old_dir) || IS_SYNC(new_dir)) { + if (buffer_dirty(old_bh)) + ll_rw_block (WRITE, 1, &old_bh); + if (ext2_osync_inode(old_inode, 1)) + retval = -EIO; + } + end_rename: brelse (dir_bh); brelse (old_bh); --- fs/ext2/truncate.c.~1~ Mon Oct 5 18:04:34 1998 +++ fs/ext2/truncate.c Thu May 27 23:57:20 1999 @@ -143,7 +143,7 @@ */ bforget(bh); if (ind_bh) - mark_buffer_dirty(ind_bh, 1); + mark_buffer_dirty_inode(ind_bh, 1, inode); ext2_free_blocks (inode, tmp, 1); goto out; } @@ -230,7 +230,7 @@ inode->i_ino, tmp); *p = 0; if (dind_bh) - mark_buffer_dirty(dind_bh, 1); + mark_buffer_dirty_inode(dind_bh, 1, inode); else mark_inode_dirty(inode); return 0; @@ -266,7 +266,7 @@ inode->i_blocks -= blocks; mark_inode_dirty(inode); bforget(bh); - mark_buffer_dirty(ind_bh, 1); + mark_buffer_dirty_inode(ind_bh, 1, inode); /* accumulate blocks to free if they're contiguous */ if (free_count == 0) @@ -312,7 +312,7 @@ inode->i_ino, tmp); *p = 0; if (tind_bh) - mark_buffer_dirty(tind_bh, 1); + mark_buffer_dirty_inode(tind_bh, 1, inode); else mark_inode_dirty(inode); return 0; @@ -425,7 +425,7 @@ if (bh) { memset (bh->b_data + offset, 0, inode->i_sb->s_blocksize - offset); - mark_buffer_dirty (bh, 0); + mark_buffer_dirty_inode (bh, 0, inode); brelse (bh); } } --- fs/inode.c.~1~ Wed May 12 15:54:22 1999 +++ fs/inode.c Fri May 28 00:12:33 1999 @@ -79,14 +79,14 @@ * In short, make sure you hash any inodes _before_ * you start marking them dirty.. */ -void __mark_inode_dirty(struct inode *inode) +void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block * sb = inode->i_sb; if (sb) { spin_lock(&inode_lock); - if (!(inode->i_state & I_DIRTY)) { - inode->i_state |= I_DIRTY; + if ((inode->i_state & flags) != flags) { + inode->i_state |= flags; /* Only add valid (ie hashed) inodes to the dirty list */ if (!list_empty(&inode->i_hash)) { list_del(&inode->i_list); @@ -129,6 +129,7 @@ init_waitqueue(&inode->i_wait); INIT_LIST_HEAD(&inode->i_hash); INIT_LIST_HEAD(&inode->i_dentry); + INIT_LIST_HEAD(&inode->i_dirty_buffers); sema_init(&inode->i_sem, 1); sema_init(&inode->i_atomic_write, 1); } @@ -149,7 +150,8 @@ list_del(&inode->i_list); list_add(&inode->i_list, &inode_in_use); /* Set I_LOCK, reset I_DIRTY */ - inode->i_state ^= I_DIRTY | I_LOCK; + inode->i_state |= I_LOCK; + inode->i_state &= ~I_DIRTY; spin_unlock(&inode_lock); write_inode(inode); @@ -194,6 +196,7 @@ spin_unlock(&inode_lock); } + /* * Called with the spinlock already held.. */ @@ -291,6 +294,7 @@ inode = list_entry(tmp, struct inode, i_list); if (inode->i_sb != sb) continue; + invalidate_inode_buffers(inode); if (!inode->i_count) { list_del(&inode->i_hash); INIT_LIST_HEAD(&inode->i_hash); @@ -336,7 +340,8 @@ * dispose_list. */ #define CAN_UNUSE(inode) \ - (((inode)->i_count | (inode)->i_state) == 0) + (((inode)->i_count | (inode)->i_state) == 0 && \ + list_empty(&(inode)->i_dirty_buffers)) #define INODE(entry) (list_entry(entry, struct inode, i_list)) static int free_inodes(void) @@ -801,5 +806,5 @@ if ( IS_NODIRATIME (inode) && S_ISDIR (inode->i_mode) ) return; if ( IS_RDONLY (inode) ) return; inode->i_atime = CURRENT_TIME; - mark_inode_dirty (inode); + mark_inode_dirty_sync (inode); } /* End Function update_atime */ --- fs/minix/fsync.c.~1~ Thu Nov 12 19:44:09 1998 +++ fs/minix/fsync.c Thu May 27 23:57:20 1999 @@ -332,7 +332,7 @@ * NULL */ -int minix_sync_file(struct file * file, struct dentry *dentry) +int minix_sync_file(struct file * file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; --- fs/ncpfs/file.c.~1~ Mon Aug 24 21:02:44 1998 +++ fs/ncpfs/file.c Thu May 27 23:57:20 1999 @@ -26,7 +26,7 @@ return a < b ? a : b; } -static int ncp_fsync(struct file *file, struct dentry *dentry) +static int ncp_fsync(struct file *file, struct dentry *dentry, int datasync) { return 0; } --- fs/nfs/file.c.~1~ Wed May 12 15:54:22 1999 +++ fs/nfs/file.c Thu May 27 23:57:20 1999 @@ -36,7 +36,7 @@ static ssize_t nfs_file_read(struct file *, char *, size_t, loff_t *); static ssize_t nfs_file_write(struct file *, const char *, size_t, loff_t *); static int nfs_file_flush(struct file *); -static int nfs_fsync(struct file *, struct dentry *dentry); +static int nfs_fsync(struct file *, struct dentry *dentry, int); static struct file_operations nfs_file_operations = { NULL, /* lseek - default */ @@ -141,7 +141,7 @@ * whether any write errors occurred for this process. */ static int -nfs_fsync(struct file *file, struct dentry *dentry) +nfs_fsync(struct file *file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; int status; --- fs/nfsd/vfs.c.~1~ Tue Apr 20 22:33:36 1999 +++ fs/nfsd/vfs.c Thu May 27 23:57:20 1999 @@ -398,7 +398,7 @@ void nfsd_sync(struct inode *inode, struct file *filp) { - filp->f_op->fsync(filp, filp->f_dentry); + filp->f_op->fsync(filp, filp->f_dentry, 0); } /* --- fs/qnx4/fsync.c.~1~ Tue Sep 1 18:43:13 1998 +++ fs/qnx4/fsync.c Thu May 27 23:57:20 1999 @@ -146,7 +146,7 @@ return err; } -int qnx4_sync_file(struct file *file, struct dentry *dentry) +int qnx4_sync_file(struct file *file, struct dentry *dentry, int datasync) { struct inode *inode = dentry->d_inode; int wait, err = 0; --- fs/smbfs/file.c.~1~ Wed Mar 17 16:12:22 1999 +++ fs/smbfs/file.c Thu May 27 23:57:20 1999 @@ -39,7 +39,7 @@ } static int -smb_fsync(struct file *file, struct dentry * dentry) +smb_fsync(struct file *file, struct dentry * dentry, int datasync) { #ifdef SMBFS_DEBUG_VERBOSE printk("smb_fsync: sync file %s/%s\n", --- fs/sysv/fsync.c.~1~ Sun Sep 13 20:12:34 1998 +++ fs/sysv/fsync.c Thu May 27 23:57:20 1999 @@ -178,7 +178,7 @@ return err; } -int sysv_sync_file(struct file * file, struct dentry *dentry) +int sysv_sync_file(struct file * file, struct dentry *dentry, int datasync) { int wait, err = 0; struct inode *inode = dentry->d_inode; --- include/linux/ext2_fs.h.~1~ Wed May 12 15:54:02 1999 +++ include/linux/ext2_fs.h Thu May 27 23:57:20 1999 @@ -544,7 +544,9 @@ extern int ext2_write (struct inode *, struct file *, char *, int); /* fsync.c */ -extern int ext2_sync_file (struct file *, struct dentry *); +extern int ext2_fsync_file (struct file *, struct dentry *, int); +extern int ext2_fsync_inode (struct inode *, int); +extern int ext2_osync_inode (struct inode *, int); /* ialloc.c */ extern struct inode * ext2_new_inode (const struct inode *, int, int *); --- include/linux/fs.h.~1~ Thu May 27 07:31:34 1999 +++ include/linux/fs.h Fri May 28 00:12:46 1999 @@ -184,38 +184,35 @@ #define BH_Protected 6 /* 1 if the buffer is protected */ /* - * Try to keep the most commonly used fields in single cache lines (16 + * Try to keep the most commonly used fields in single cache lines (32 * bytes) to improve performance. This ordering should be - * particularly beneficial on 32-bit processors. - * - * We use the first 16 bytes for the data which is used in searches - * over the block hash lists (ie. getblk(), find_buffer() and - * friends). - * - * The second 16 bytes we use for lru buffer scans, as used by - * sync_buffers() and refill_freelist(). -- sct + * particularly beneficial on 32-bit processors. -- sct */ struct buffer_head { /* First cache line: */ struct buffer_head * b_next; /* Hash queue list */ unsigned long b_blocknr; /* block number */ - unsigned long b_size; /* block size */ kdev_t b_dev; /* device (B_FREE = free) */ kdev_t b_rdev; /* Real device */ - unsigned long b_rsector; /* Real buffer location on disk */ struct buffer_head * b_this_page; /* circular list of buffers in one page */ unsigned long b_state; /* buffer state bitmap (see above) */ struct buffer_head * b_next_free; unsigned int b_count; /* users using this block */ + unsigned int b_list; /* List that this buffer appears */ /* Non-performance-critical data follows. */ + unsigned long b_size; /* block size */ + unsigned long b_rsector; /* Real buffer location on disk */ char * b_data; /* pointer to data block (1024 bytes) */ - unsigned int b_list; /* List that this buffer appears */ unsigned long b_flushtime; /* Time when this (dirty) buffer * should be written */ struct wait_queue * b_wait; struct buffer_head ** b_pprev; /* doubly linked list of hash-queue */ struct buffer_head * b_prev_free; /* doubly linked list of buffers */ + + struct inode * b_inode; + struct list_head b_inode_buffers; /* doubly linked list of inode dirty buffers */ + struct buffer_head * b_reqnext; /* request queue */ /* @@ -333,6 +330,8 @@ struct list_head i_list; struct list_head i_dentry; + struct list_head i_dirty_buffers; + unsigned long i_ino; unsigned int i_count; kdev_t i_dev; @@ -358,7 +357,7 @@ struct vm_area_struct *i_mmap; struct page *i_pages; struct dquot *i_dquot[MAXQUOTAS]; - + unsigned long i_state; unsigned int i_flags; @@ -393,15 +392,23 @@ }; /* Inode state bits.. */ -#define I_DIRTY 1 -#define I_LOCK 2 -#define I_FREEING 4 +#define I_DIRTY_SYNC 1 /* Not dirty enough for O_DATASYNC */ +#define I_DIRTY_DATASYNC 2 /* Data-related inode changes pending */ +#define I_LOCK 4 +#define I_FREEING 8 -extern void __mark_inode_dirty(struct inode *); +#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC) + +extern void __mark_inode_dirty(struct inode *, int); static inline void mark_inode_dirty(struct inode *inode) { - if (!(inode->i_state & I_DIRTY)) - __mark_inode_dirty(inode); + if ((inode->i_state & I_DIRTY) != I_DIRTY) + __mark_inode_dirty(inode, I_DIRTY); +} +static inline void mark_inode_dirty_sync(struct inode *inode) +{ + if (!(inode->i_state & I_DIRTY_SYNC)) + __mark_inode_dirty(inode, I_DIRTY_SYNC); } struct fown_struct { @@ -587,7 +594,7 @@ int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); - int (*fsync) (struct file *, struct dentry *); + int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*check_media_change) (kdev_t dev); int (*revalidate) (kdev_t dev); @@ -775,15 +782,25 @@ } } +extern void buffer_insert_inode_queue(struct buffer_head *, struct inode *); +static inline void mark_buffer_dirty_inode(struct buffer_head *bh, int flag, struct inode *inode) +{ + buffer_insert_inode_queue(bh, inode); + mark_buffer_dirty(bh, flag); +} + extern int check_disk_change(kdev_t dev); extern int invalidate_inodes(struct super_block * sb); extern void invalidate_inode_pages(struct inode *); +extern void invalidate_inode_buffers(struct inode *); extern void invalidate_buffers(kdev_t dev); extern int floppy_is_wp(int minor); extern void sync_inodes(kdev_t dev); extern void write_inode_now(struct inode *inode); extern void sync_dev(kdev_t dev); extern int fsync_dev(kdev_t dev); +extern int fsync_inode_buffers(struct inode *); +extern int osync_inode_buffers(struct inode *); extern void sync_supers(kdev_t dev); extern int bmap(struct inode * inode,int block); extern int notify_change(struct dentry *, struct iattr *); @@ -887,8 +904,8 @@ extern ssize_t char_write(struct file *, const char *, size_t, loff_t *); extern ssize_t block_write(struct file *, const char *, size_t, loff_t *); -extern int block_fsync(struct file *, struct dentry *dir); -extern int file_fsync(struct file *, struct dentry *dir); +extern int block_fsync(struct file *, struct dentry *dir, int); +extern int file_fsync(struct file *, struct dentry *dir, int); extern int inode_change_ok(struct inode *, struct iattr *); extern void inode_setattr(struct inode *, struct iattr *); --- include/linux/minix_fs.h.~1~ Wed May 12 15:54:03 1999 +++ include/linux/minix_fs.h Thu May 27 23:57:20 1999 @@ -115,7 +115,7 @@ extern void minix_truncate(struct inode *); extern int init_minix_fs(void); extern int minix_sync_inode(struct inode *); -extern int minix_sync_file(struct file *, struct dentry *); +extern int minix_sync_file(struct file *, struct dentry *, int); extern struct inode_operations minix_file_inode_operations; extern struct inode_operations minix_dir_inode_operations; --- include/linux/qnx4_fs.h.~1~ Wed May 12 15:54:03 1999 +++ include/linux/qnx4_fs.h Thu May 27 23:57:20 1999 @@ -114,7 +114,7 @@ extern void qnx4_free_inode(struct inode *inode); extern int qnx4_unlink(struct inode *dir, struct dentry *dentry); extern int qnx4_rmdir(struct inode *dir, struct dentry *dentry); -extern int qnx4_sync_file(struct file *file, struct dentry *dentry); +extern int qnx4_sync_file(struct file *file, struct dentry *dentry, int); extern int qnx4_sync_inode(struct inode *inode); extern int qnx4_bmap(struct inode *inode, int block); --- include/linux/sysv_fs.h.~1~ Thu May 27 07:45:52 1999 +++ include/linux/sysv_fs.h Fri May 28 00:22:49 1999 @@ -400,7 +400,7 @@ extern void sysv_write_inode(struct inode *); extern int sysv_statfs(struct super_block *, struct statfs *, int); extern int sysv_sync_inode(struct inode *); -extern int sysv_sync_file(struct file *, struct dentry *); +extern int sysv_sync_file(struct file *, struct dentry *, int); extern int sysv_mmap(struct file *, struct vm_area_struct *); extern struct inode_operations sysv_file_inode_operations; --- mm/filemap.c.~1~ Wed May 12 15:54:27 1999 +++ mm/filemap.c Thu May 27 23:57:20 1999 @@ -1350,7 +1350,10 @@ struct dentry * dentry = file->f_dentry; struct inode * inode = dentry->d_inode; down(&inode->i_sem); - error = file_fsync(file, dentry); + /* According to SingleUnix, we are only + * required to implement fdatasync, not + * fsync, consistency here. */ + error = file_fsync(file, dentry, 1); up(&inode->i_sem); } } .