fs: Add helper to clean bdev aliases under a bh and use it
[sfrench/cifs-2.6.git] / fs / ext4 / inode.c
index c6ea25a190f88b09354e93cb7245586af2dbc87a..2f8127601befc0f47bace3080ff6ec1975935d43 100644 (file)
@@ -647,11 +647,15 @@ found:
                /*
                 * We have to zeroout blocks before inserting them into extent
                 * status tree. Otherwise someone could look them up there and
-                * use them before they are really zeroed.
+                * use them before they are really zeroed. We also have to
+                * unmap metadata before zeroing as otherwise writeback can
+                * overwrite zeros with stale data from block device.
                 */
                if (flags & EXT4_GET_BLOCKS_ZERO &&
                    map->m_flags & EXT4_MAP_MAPPED &&
                    map->m_flags & EXT4_MAP_NEW) {
+                       clean_bdev_aliases(inode->i_sb->s_bdev, map->m_pblk,
+                                          map->m_len);
                        ret = ext4_issue_zeroout(inode, map->m_lblk,
                                                 map->m_pblk, map->m_len);
                        if (ret) {
@@ -1119,8 +1123,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len,
                        if (err)
                                break;
                        if (buffer_new(bh)) {
-                               unmap_underlying_metadata(bh->b_bdev,
-                                                         bh->b_blocknr);
+                               clean_bdev_bh_alias(bh);
                                if (PageUptodate(page)) {
                                        clear_buffer_new(bh);
                                        set_buffer_uptodate(bh);
@@ -1649,6 +1652,8 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd,
                        BUG_ON(!PageLocked(page));
                        BUG_ON(PageWriteback(page));
                        if (invalidate) {
+                               if (page_mapped(page))
+                                       clear_page_dirty_for_io(page);
                                block_invalidatepage(page, 0, PAGE_SIZE);
                                ClearPageUptodate(page);
                        }
@@ -2350,11 +2355,8 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
 
        BUG_ON(map->m_len == 0);
        if (map->m_flags & EXT4_MAP_NEW) {
-               struct block_device *bdev = inode->i_sb->s_bdev;
-               int i;
-
-               for (i = 0; i < map->m_len; i++)
-                       unmap_underlying_metadata(bdev, map->m_pblk + i);
+               clean_bdev_aliases(inode->i_sb->s_bdev, map->m_pblk,
+                                  map->m_len);
        }
        return 0;
 }
@@ -3526,35 +3528,31 @@ out:
 
 static ssize_t ext4_direct_IO_read(struct kiocb *iocb, struct iov_iter *iter)
 {
-       int unlocked = 0;
-       struct inode *inode = iocb->ki_filp->f_mapping->host;
+       struct address_space *mapping = iocb->ki_filp->f_mapping;
+       struct inode *inode = mapping->host;
        ssize_t ret;
 
-       if (ext4_should_dioread_nolock(inode)) {
-               /*
-                * Nolock dioread optimization may be dynamically disabled
-                * via ext4_inode_block_unlocked_dio(). Check inode's state
-                * while holding extra i_dio_count ref.
-                */
-               inode_dio_begin(inode);
-               smp_mb();
-               if (unlikely(ext4_test_inode_state(inode,
-                                                   EXT4_STATE_DIOREAD_LOCK)))
-                       inode_dio_end(inode);
-               else
-                       unlocked = 1;
-       }
+       /*
+        * Shared inode_lock is enough for us - it protects against concurrent
+        * writes & truncates and since we take care of writing back page cache,
+        * we are protected against page writeback as well.
+        */
+       inode_lock_shared(inode);
        if (IS_DAX(inode)) {
-               ret = dax_do_io(iocb, inode, iter, ext4_dio_get_block,
-                               NULL, unlocked ? 0 : DIO_LOCKING);
+               ret = dax_do_io(iocb, inode, iter, ext4_dio_get_block, NULL, 0);
        } else {
+               size_t count = iov_iter_count(iter);
+
+               ret = filemap_write_and_wait_range(mapping, iocb->ki_pos,
+                                                  iocb->ki_pos + count);
+               if (ret)
+                       goto out_unlock;
                ret = __blockdev_direct_IO(iocb, inode, inode->i_sb->s_bdev,
                                           iter, ext4_dio_get_block,
-                                          NULL, NULL,
-                                          unlocked ? 0 : DIO_LOCKING);
+                                          NULL, NULL, 0);
        }
-       if (unlocked)
-               inode_dio_end(inode);
+out_unlock:
+       inode_unlock_shared(inode);
        return ret;
 }
 
@@ -3890,7 +3888,7 @@ int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset,
 }
 
 /*
- * ext4_punch_hole: punches a hole in a file by releaseing the blocks
+ * ext4_punch_hole: punches a hole in a file by releasing the blocks
  * associated with the given offset and length
  *
  * @inode:  File inode
@@ -3919,7 +3917,7 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)
         * Write out all dirty pages to avoid race conditions
         * Then release them.
         */
-       if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
+       if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
                ret = filemap_write_and_wait_range(mapping, offset,
                                                   offset + length - 1);
                if (ret)
@@ -4414,7 +4412,7 @@ static inline void ext4_iget_extra_inode(struct inode *inode,
 
 int ext4_get_projid(struct inode *inode, kprojid_t *projid)
 {
-       if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_PROJECT))
+       if (!ext4_has_feature_project(inode->i_sb))
                return -EOPNOTSUPP;
        *projid = EXT4_I(inode)->i_projid;
        return 0;
@@ -4481,7 +4479,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
        inode->i_mode = le16_to_cpu(raw_inode->i_mode);
        i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
        i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
-       if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+       if (ext4_has_feature_project(sb) &&
            EXT4_INODE_SIZE(sb) > EXT4_GOOD_OLD_INODE_SIZE &&
            EXT4_FITS_IN_INODE(raw_inode, ei, i_projid))
                i_projid = (projid_t)le32_to_cpu(raw_inode->i_projid);
@@ -4814,14 +4812,14 @@ static int ext4_do_update_inode(handle_t *handle,
  * Fix up interoperability with old kernels. Otherwise, old inodes get
  * re-used with the upper 16 bits of the uid/gid intact
  */
-               if (!ei->i_dtime) {
+               if (ei->i_dtime && list_empty(&ei->i_orphan)) {
+                       raw_inode->i_uid_high = 0;
+                       raw_inode->i_gid_high = 0;
+               } else {
                        raw_inode->i_uid_high =
                                cpu_to_le16(high_16_bits(i_uid));
                        raw_inode->i_gid_high =
                                cpu_to_le16(high_16_bits(i_gid));
-               } else {
-                       raw_inode->i_uid_high = 0;
-                       raw_inode->i_gid_high = 0;
                }
        } else {
                raw_inode->i_uid_low = cpu_to_le16(fs_high2lowuid(i_uid));
@@ -4885,8 +4883,7 @@ static int ext4_do_update_inode(handle_t *handle,
                }
        }
 
-       BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
-                       EXT4_FEATURE_RO_COMPAT_PROJECT) &&
+       BUG_ON(!ext4_has_feature_project(inode->i_sb) &&
               i_projid != EXT4_DEF_PROJID);
 
        if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE &&
@@ -5073,7 +5070,7 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
        int orphan = 0;
        const unsigned int ia_valid = attr->ia_valid;
 
-       error = inode_change_ok(inode, attr);
+       error = setattr_prepare(dentry, attr);
        if (error)
                return error;