]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/ext4/file.c
ext4: fix ext4_flush_completed_IO wait semantics
[karo-tx-linux.git] / fs / ext4 / file.c
index 782eecb57e43a33ed5d6485f9490140ae6d88255..ca6f07afe60154ecd3b996946e78173e52000487 100644 (file)
@@ -55,11 +55,11 @@ static int ext4_release_file(struct inode *inode, struct file *filp)
        return 0;
 }
 
-static void ext4_aiodio_wait(struct inode *inode)
+void ext4_unwritten_wait(struct inode *inode)
 {
        wait_queue_head_t *wq = ext4_ioend_wq(inode);
 
-       wait_event(*wq, (atomic_read(&EXT4_I(inode)->i_aiodio_unwritten) == 0));
+       wait_event(*wq, (atomic_read(&EXT4_I(inode)->i_unwritten) == 0));
 }
 
 /*
@@ -89,12 +89,92 @@ ext4_unaligned_aio(struct inode *inode, const struct iovec *iov,
        return 0;
 }
 
+static ssize_t
+ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,
+                   unsigned long nr_segs, loff_t pos)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_mapping->host;
+       struct blk_plug plug;
+       int unaligned_aio = 0;
+       ssize_t ret;
+       int overwrite = 0;
+       size_t length = iov_length(iov, nr_segs);
+
+       if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS) &&
+           !is_sync_kiocb(iocb))
+               unaligned_aio = ext4_unaligned_aio(inode, iov, nr_segs, pos);
+
+       /* Unaligned direct AIO must be serialized; see comment above */
+       if (unaligned_aio) {
+               static unsigned long unaligned_warn_time;
+
+               /* Warn about this once per day */
+               if (printk_timed_ratelimit(&unaligned_warn_time, 60*60*24*HZ))
+                       ext4_msg(inode->i_sb, KERN_WARNING,
+                                "Unaligned AIO/DIO on inode %ld by %s; "
+                                "performance will be poor.",
+                                inode->i_ino, current->comm);
+               mutex_lock(ext4_aio_mutex(inode));
+               ext4_unwritten_wait(inode);
+       }
+
+       BUG_ON(iocb->ki_pos != pos);
+
+       mutex_lock(&inode->i_mutex);
+       blk_start_plug(&plug);
+
+       iocb->private = &overwrite;
+
+       /* check whether we do a DIO overwrite or not */
+       if (ext4_should_dioread_nolock(inode) && !unaligned_aio &&
+           !file->f_mapping->nrpages && pos + length <= i_size_read(inode)) {
+               struct ext4_map_blocks map;
+               unsigned int blkbits = inode->i_blkbits;
+               int err, len;
+
+               map.m_lblk = pos >> blkbits;
+               map.m_len = (EXT4_BLOCK_ALIGN(pos + length, blkbits) >> blkbits)
+                       - map.m_lblk;
+               len = map.m_len;
+
+               err = ext4_map_blocks(NULL, inode, &map, 0);
+               /*
+                * 'err==len' means that all of blocks has been preallocated no
+                * matter they are initialized or not.  For excluding
+                * uninitialized extents, we need to check m_flags.  There are
+                * two conditions that indicate for initialized extents.
+                * 1) If we hit extent cache, EXT4_MAP_MAPPED flag is returned;
+                * 2) If we do a real lookup, non-flags are returned.
+                * So we should check these two conditions.
+                */
+               if (err == len && (map.m_flags & EXT4_MAP_MAPPED))
+                       overwrite = 1;
+       }
+
+       ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
+       mutex_unlock(&inode->i_mutex);
+
+       if (ret > 0 || ret == -EIOCBQUEUED) {
+               ssize_t err;
+
+               err = generic_write_sync(file, pos, ret);
+               if (err < 0 && ret > 0)
+                       ret = err;
+       }
+       blk_finish_plug(&plug);
+
+       if (unaligned_aio)
+               mutex_unlock(ext4_aio_mutex(inode));
+
+       return ret;
+}
+
 static ssize_t
 ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
                unsigned long nr_segs, loff_t pos)
 {
        struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
-       int unaligned_aio = 0;
        ssize_t ret;
 
        /*
@@ -114,29 +194,12 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
                        nr_segs = iov_shorten((struct iovec *)iov, nr_segs,
                                              sbi->s_bitmap_maxbytes - pos);
                }
-       } else if (unlikely((iocb->ki_filp->f_flags & O_DIRECT) &&
-                  !is_sync_kiocb(iocb))) {
-               unaligned_aio = ext4_unaligned_aio(inode, iov, nr_segs, pos);
-       }
-
-       /* Unaligned direct AIO must be serialized; see comment above */
-       if (unaligned_aio) {
-               static unsigned long unaligned_warn_time;
-
-               /* Warn about this once per day */
-               if (printk_timed_ratelimit(&unaligned_warn_time, 60*60*24*HZ))
-                       ext4_msg(inode->i_sb, KERN_WARNING,
-                                "Unaligned AIO/DIO on inode %ld by %s; "
-                                "performance will be poor.",
-                                inode->i_ino, current->comm);
-               mutex_lock(ext4_aio_mutex(inode));
-               ext4_aiodio_wait(inode);
        }
 
-       ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
-
-       if (unaligned_aio)
-               mutex_unlock(ext4_aio_mutex(inode));
+       if (unlikely(iocb->ki_filp->f_flags & O_DIRECT))
+               ret = ext4_file_dio_write(iocb, iov, nr_segs, pos);
+       else
+               ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
 
        return ret;
 }
@@ -181,9 +244,21 @@ static int ext4_file_open(struct inode * inode, struct file * filp)
                path.dentry = mnt->mnt_root;
                cp = d_path(&path, buf, sizeof(buf));
                if (!IS_ERR(cp)) {
+                       handle_t *handle;
+                       int err;
+
+                       handle = ext4_journal_start_sb(sb, 1);
+                       if (IS_ERR(handle))
+                               return PTR_ERR(handle);
+                       err = ext4_journal_get_write_access(handle, sbi->s_sbh);
+                       if (err) {
+                               ext4_journal_stop(handle);
+                               return err;
+                       }
                        strlcpy(sbi->s_es->s_last_mounted, cp,
                                sizeof(sbi->s_es->s_last_mounted));
-                       ext4_mark_super_dirty(sb);
+                       ext4_handle_dirty_super(handle, sb);
+                       ext4_journal_stop(handle);
                }
        }
        /*