Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5439ca6b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ext4 bug fixes from Ted Ts'o:
 "Various bug fixes for ext4.  Perhaps the most serious bug fixed is one
  which could cause file system corruptions when performing file punch
  operations."

* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: avoid hang when mounting non-journal filesystems with orphan list
  ext4: lock i_mutex when truncating orphan inodes
  ext4: do not try to write superblock on ro remount w/o journal
  ext4: include journal blocks in df overhead calcs
  ext4: remove unaligned AIO warning printk
  ext4: fix an incorrect comment about i_mutex
  ext4: fix deadlock in journal_unmap_buffer()
  ext4: split off ext4_journalled_invalidatepage()
  jbd2: fix assertion failure in jbd2_journal_flush()
  ext4: check dioread_nolock on remount
  ext4: fix extent tree corruption caused by hole punch
parents a7a88b23 0e9a9a1a
Loading
Loading
Loading
Loading
+18 −4
Original line number Original line Diff line number Diff line
@@ -2226,13 +2226,14 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
 * removes index from the index block.
 * removes index from the index block.
 */
 */
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
			struct ext4_ext_path *path)
			struct ext4_ext_path *path, int depth)
{
{
	int err;
	int err;
	ext4_fsblk_t leaf;
	ext4_fsblk_t leaf;


	/* free index block */
	/* free index block */
	path--;
	depth--;
	path = path + depth;
	leaf = ext4_idx_pblock(path->p_idx);
	leaf = ext4_idx_pblock(path->p_idx);
	if (unlikely(path->p_hdr->eh_entries == 0)) {
	if (unlikely(path->p_hdr->eh_entries == 0)) {
		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
@@ -2257,6 +2258,19 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,


	ext4_free_blocks(handle, inode, NULL, leaf, 1,
	ext4_free_blocks(handle, inode, NULL, leaf, 1,
			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);

	while (--depth >= 0) {
		if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr))
			break;
		path--;
		err = ext4_ext_get_access(handle, inode, path);
		if (err)
			break;
		path->p_idx->ei_block = (path+1)->p_idx->ei_block;
		err = ext4_ext_dirty(handle, inode, path);
		if (err)
			break;
	}
	return err;
	return err;
}
}


@@ -2599,7 +2613,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
	/* if this leaf is free, then we should
	/* if this leaf is free, then we should
	 * remove it from index block above */
	 * remove it from index block above */
	if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
	if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
		err = ext4_ext_rm_idx(handle, inode, path + depth);
		err = ext4_ext_rm_idx(handle, inode, path, depth);


out:
out:
	return err;
	return err;
@@ -2802,7 +2816,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
				/* index is empty, remove it;
				/* index is empty, remove it;
				 * handle must be already prepared by the
				 * handle must be already prepared by the
				 * truncatei_leaf() */
				 * truncatei_leaf() */
				err = ext4_ext_rm_idx(handle, inode, path + i);
				err = ext4_ext_rm_idx(handle, inode, path, i);
			}
			}
			/* root level has p_bh == NULL, brelse() eats this */
			/* root level has p_bh == NULL, brelse() eats this */
			brelse(path[i].p_bh);
			brelse(path[i].p_bh);
+0 −8
Original line number Original line Diff line number Diff line
@@ -108,14 +108,6 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,


	/* Unaligned direct AIO must be serialized; see comment above */
	/* Unaligned direct AIO must be serialized; see comment above */
	if (unaligned_aio) {
	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));
		mutex_lock(ext4_aio_mutex(inode));
		ext4_unwritten_wait(inode);
		ext4_unwritten_wait(inode);
	}
	}
+0 −2
Original line number Original line Diff line number Diff line
@@ -109,8 +109,6 @@ static int __sync_inode(struct inode *inode, int datasync)
 *
 *
 * What we do is just kick off a commit and wait on it.  This will snapshot the
 * What we do is just kick off a commit and wait on it.  This will snapshot the
 * inode to disk.
 * inode to disk.
 *
 * i_mutex lock is held when entering and exiting this function
 */
 */


int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
+84 −15
Original line number Original line Diff line number Diff line
@@ -2880,8 +2880,6 @@ static void ext4_invalidatepage_free_endio(struct page *page, unsigned long offs


static void ext4_invalidatepage(struct page *page, unsigned long offset)
static void ext4_invalidatepage(struct page *page, unsigned long offset)
{
{
	journal_t *journal = EXT4_JOURNAL(page->mapping->host);

	trace_ext4_invalidatepage(page, offset);
	trace_ext4_invalidatepage(page, offset);


	/*
	/*
@@ -2889,16 +2887,34 @@ static void ext4_invalidatepage(struct page *page, unsigned long offset)
	 */
	 */
	if (ext4_should_dioread_nolock(page->mapping->host))
	if (ext4_should_dioread_nolock(page->mapping->host))
		ext4_invalidatepage_free_endio(page, offset);
		ext4_invalidatepage_free_endio(page, offset);

	/* No journalling happens on data buffers when this function is used */
	WARN_ON(page_has_buffers(page) && buffer_jbd(page_buffers(page)));

	block_invalidatepage(page, offset);
}

static int __ext4_journalled_invalidatepage(struct page *page,
					    unsigned long offset)
{
	journal_t *journal = EXT4_JOURNAL(page->mapping->host);

	trace_ext4_journalled_invalidatepage(page, offset);

	/*
	/*
	 * If it's a full truncate we just forget about the pending dirtying
	 * If it's a full truncate we just forget about the pending dirtying
	 */
	 */
	if (offset == 0)
	if (offset == 0)
		ClearPageChecked(page);
		ClearPageChecked(page);


	if (journal)
	return jbd2_journal_invalidatepage(journal, page, offset);
		jbd2_journal_invalidatepage(journal, page, offset);
}
	else

		block_invalidatepage(page, offset);
/* Wrapper for aops... */
static void ext4_journalled_invalidatepage(struct page *page,
					   unsigned long offset)
{
	WARN_ON(__ext4_journalled_invalidatepage(page, offset) < 0);
}
}


static int ext4_releasepage(struct page *page, gfp_t wait)
static int ext4_releasepage(struct page *page, gfp_t wait)
@@ -3264,7 +3280,7 @@ static const struct address_space_operations ext4_journalled_aops = {
	.write_end		= ext4_journalled_write_end,
	.write_end		= ext4_journalled_write_end,
	.set_page_dirty		= ext4_journalled_set_page_dirty,
	.set_page_dirty		= ext4_journalled_set_page_dirty,
	.bmap			= ext4_bmap,
	.bmap			= ext4_bmap,
	.invalidatepage		= ext4_invalidatepage,
	.invalidatepage		= ext4_journalled_invalidatepage,
	.releasepage		= ext4_releasepage,
	.releasepage		= ext4_releasepage,
	.direct_IO		= ext4_direct_IO,
	.direct_IO		= ext4_direct_IO,
	.is_partially_uptodate  = block_is_partially_uptodate,
	.is_partially_uptodate  = block_is_partially_uptodate,
@@ -4304,6 +4320,47 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc)
	return err;
	return err;
}
}


/*
 * In data=journal mode ext4_journalled_invalidatepage() may fail to invalidate
 * buffers that are attached to a page stradding i_size and are undergoing
 * commit. In that case we have to wait for commit to finish and try again.
 */
static void ext4_wait_for_tail_page_commit(struct inode *inode)
{
	struct page *page;
	unsigned offset;
	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
	tid_t commit_tid = 0;
	int ret;

	offset = inode->i_size & (PAGE_CACHE_SIZE - 1);
	/*
	 * All buffers in the last page remain valid? Then there's nothing to
	 * do. We do the check mainly to optimize the common PAGE_CACHE_SIZE ==
	 * blocksize case
	 */
	if (offset > PAGE_CACHE_SIZE - (1 << inode->i_blkbits))
		return;
	while (1) {
		page = find_lock_page(inode->i_mapping,
				      inode->i_size >> PAGE_CACHE_SHIFT);
		if (!page)
			return;
		ret = __ext4_journalled_invalidatepage(page, offset);
		unlock_page(page);
		page_cache_release(page);
		if (ret != -EBUSY)
			return;
		commit_tid = 0;
		read_lock(&journal->j_state_lock);
		if (journal->j_committing_transaction)
			commit_tid = journal->j_committing_transaction->t_tid;
		read_unlock(&journal->j_state_lock);
		if (commit_tid)
			jbd2_log_wait_commit(journal, commit_tid);
	}
}

/*
/*
 * ext4_setattr()
 * ext4_setattr()
 *
 *
@@ -4417,16 +4474,28 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
	}
	}


	if (attr->ia_valid & ATTR_SIZE) {
	if (attr->ia_valid & ATTR_SIZE) {
		if (attr->ia_size != i_size_read(inode)) {
		if (attr->ia_size != inode->i_size) {
			truncate_setsize(inode, attr->ia_size);
			loff_t oldsize = inode->i_size;
			/* Inode size will be reduced, wait for dio in flight.

			 * Temporarily disable dioread_nolock to prevent
			i_size_write(inode, attr->ia_size);
			 * livelock. */
			/*
			 * Blocks are going to be removed from the inode. Wait
			 * for dio in flight.  Temporarily disable
			 * dioread_nolock to prevent livelock.
			 */
			if (orphan) {
			if (orphan) {
				if (!ext4_should_journal_data(inode)) {
					ext4_inode_block_unlocked_dio(inode);
					ext4_inode_block_unlocked_dio(inode);
					inode_dio_wait(inode);
					inode_dio_wait(inode);
					ext4_inode_resume_unlocked_dio(inode);
					ext4_inode_resume_unlocked_dio(inode);
				} else
					ext4_wait_for_tail_page_commit(inode);
			}
			}
			/*
			 * Truncate pagecache after we've waited for commit
			 * in data=journal mode to make pages freeable.
			 */
			truncate_pagecache(inode, oldsize, inode->i_size);
		}
		}
		ext4_truncate(inode);
		ext4_truncate(inode);
	}
	}
+2 −1
Original line number Original line Diff line number Diff line
@@ -2648,7 +2648,8 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
	struct ext4_iloc iloc;
	struct ext4_iloc iloc;
	int err = 0;
	int err = 0;


	if (!EXT4_SB(inode->i_sb)->s_journal)
	if ((!EXT4_SB(inode->i_sb)->s_journal) &&
	    !(EXT4_SB(inode->i_sb)->s_mount_state & EXT4_ORPHAN_FS))
		return 0;
		return 0;


	mutex_lock(&EXT4_SB(inode->i_sb)->s_orphan_lock);
	mutex_lock(&EXT4_SB(inode->i_sb)->s_orphan_lock);
Loading