diff options
| -rw-r--r-- | fs/ext3/super.c | 8 | ||||
| -rw-r--r-- | fs/jbd/commit.c | 45 | ||||
| -rw-r--r-- | fs/jbd/transaction.c | 64 | ||||
| -rw-r--r-- | fs/reiserfs/xattr.c | 2 | ||||
| -rw-r--r-- | fs/udf/file.c | 9 | ||||
| -rw-r--r-- | fs/udf/inode.c | 57 | 
6 files changed, 135 insertions, 50 deletions
diff --git a/fs/ext3/super.c b/fs/ext3/super.c index bd29894c8fb..17ae5c83d23 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -980,7 +980,7 @@ static int parse_options (char *options, struct super_block *sb,  		 * Initialize args struct so we know whether arg was  		 * found; some options take optional arguments.  		 */ -		args[0].to = args[0].from = 0; +		args[0].to = args[0].from = NULL;  		token = match_token(p, tokens, args);  		switch (token) {  		case Opt_bsd_df: @@ -1484,10 +1484,12 @@ static void ext3_orphan_cleanup (struct super_block * sb,  	}  	if (EXT3_SB(sb)->s_mount_state & EXT3_ERROR_FS) { -		if (es->s_last_orphan) +		/* don't clear list on RO mount w/ errors */ +		if (es->s_last_orphan && !(s_flags & MS_RDONLY)) {  			jbd_debug(1, "Errors on filesystem, "  				  "clearing orphan list.\n"); -		es->s_last_orphan = 0; +			es->s_last_orphan = 0; +		}  		jbd_debug(1, "Skipping orphan recovery on fs with errors.\n");  		return;  	} diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 52c15c77602..86b39b167c2 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -86,7 +86,12 @@ nope:  static void release_data_buffer(struct buffer_head *bh)  {  	if (buffer_freed(bh)) { +		WARN_ON_ONCE(buffer_dirty(bh));  		clear_buffer_freed(bh); +		clear_buffer_mapped(bh); +		clear_buffer_new(bh); +		clear_buffer_req(bh); +		bh->b_bdev = NULL;  		release_buffer_page(bh);  	} else  		put_bh(bh); @@ -866,17 +871,35 @@ restart_loop:  		 * there's no point in keeping a checkpoint record for  		 * it. */ -		/* A buffer which has been freed while still being -		 * journaled by a previous transaction may end up still -		 * being dirty here, but we want to avoid writing back -		 * that buffer in the future after the "add to orphan" -		 * operation been committed,  That's not only a performance -		 * gain, it also stops aliasing problems if the buffer is -		 * left behind for writeback and gets reallocated for another -		 * use in a different page. */ -		if (buffer_freed(bh) && !jh->b_next_transaction) { -			clear_buffer_freed(bh); -			clear_buffer_jbddirty(bh); +		/* +		 * A buffer which has been freed while still being journaled by +		 * a previous transaction. +		 */ +		if (buffer_freed(bh)) { +			/* +			 * If the running transaction is the one containing +			 * "add to orphan" operation (b_next_transaction != +			 * NULL), we have to wait for that transaction to +			 * commit before we can really get rid of the buffer. +			 * So just clear b_modified to not confuse transaction +			 * credit accounting and refile the buffer to +			 * BJ_Forget of the running transaction. If the just +			 * committed transaction contains "add to orphan" +			 * operation, we can completely invalidate the buffer +			 * now. We are rather throughout in that since the +			 * buffer may be still accessible when blocksize < +			 * pagesize and it is attached to the last partial +			 * page. +			 */ +			jh->b_modified = 0; +			if (!jh->b_next_transaction) { +				clear_buffer_freed(bh); +				clear_buffer_jbddirty(bh); +				clear_buffer_mapped(bh); +				clear_buffer_new(bh); +				clear_buffer_req(bh); +				bh->b_bdev = NULL; +			}  		}  		if (buffer_jbddirty(bh)) { diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index febc10db5ce..78b7f84241d 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1843,15 +1843,16 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)   * We're outside-transaction here.  Either or both of j_running_transaction   * and j_committing_transaction may be NULL.   */ -static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) +static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh, +				int partial_page)  {  	transaction_t *transaction;  	struct journal_head *jh;  	int may_free = 1; -	int ret;  	BUFFER_TRACE(bh, "entry"); +retry:  	/*  	 * It is safe to proceed here without the j_list_lock because the  	 * buffers cannot be stolen by try_to_free_buffers as long as we are @@ -1879,10 +1880,18 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)  	 * clear the buffer dirty bit at latest at the moment when the  	 * transaction marking the buffer as freed in the filesystem  	 * structures is committed because from that moment on the -	 * buffer can be reallocated and used by a different page. +	 * block can be reallocated and used by a different page.  	 * Since the block hasn't been freed yet but the inode has  	 * already been added to orphan list, it is safe for us to add  	 * the buffer to BJ_Forget list of the newest transaction. +	 * +	 * Also we have to clear buffer_mapped flag of a truncated buffer +	 * because the buffer_head may be attached to the page straddling +	 * i_size (can happen only when blocksize < pagesize) and thus the +	 * buffer_head can be reused when the file is extended again. So we end +	 * up keeping around invalidated buffers attached to transactions' +	 * BJ_Forget list just to stop checkpointing code from cleaning up +	 * the transaction this buffer was modified in.  	 */  	transaction = jh->b_transaction;  	if (transaction == NULL) { @@ -1909,13 +1918,9 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)  			 * committed, the buffer won't be needed any  			 * longer. */  			JBUFFER_TRACE(jh, "checkpointed: add to BJ_Forget"); -			ret = __dispose_buffer(jh, +			may_free = __dispose_buffer(jh,  					journal->j_running_transaction); -			journal_put_journal_head(jh); -			spin_unlock(&journal->j_list_lock); -			jbd_unlock_bh_state(bh); -			spin_unlock(&journal->j_state_lock); -			return ret; +			goto zap_buffer;  		} else {  			/* There is no currently-running transaction. So the  			 * orphan record which we wrote for this file must have @@ -1923,13 +1928,9 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)  			 * the committing transaction, if it exists. */  			if (journal->j_committing_transaction) {  				JBUFFER_TRACE(jh, "give to committing trans"); -				ret = __dispose_buffer(jh, +				may_free = __dispose_buffer(jh,  					journal->j_committing_transaction); -				journal_put_journal_head(jh); -				spin_unlock(&journal->j_list_lock); -				jbd_unlock_bh_state(bh); -				spin_unlock(&journal->j_state_lock); -				return ret; +				goto zap_buffer;  			} else {  				/* The orphan record's transaction has  				 * committed.  We can cleanse this buffer */ @@ -1950,10 +1951,24 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)  		}  		/*  		 * The buffer is committing, we simply cannot touch -		 * it. So we just set j_next_transaction to the -		 * running transaction (if there is one) and mark -		 * buffer as freed so that commit code knows it should -		 * clear dirty bits when it is done with the buffer. +		 * it. If the page is straddling i_size we have to wait +		 * for commit and try again. +		 */ +		if (partial_page) { +			tid_t tid = journal->j_committing_transaction->t_tid; + +			journal_put_journal_head(jh); +			spin_unlock(&journal->j_list_lock); +			jbd_unlock_bh_state(bh); +			spin_unlock(&journal->j_state_lock); +			log_wait_commit(journal, tid); +			goto retry; +		} +		/* +		 * OK, buffer won't be reachable after truncate. We just set +		 * j_next_transaction to the running transaction (if there is +		 * one) and mark buffer as freed so that commit code knows it +		 * should clear dirty bits when it is done with the buffer.  		 */  		set_buffer_freed(bh);  		if (journal->j_running_transaction && buffer_jbddirty(bh)) @@ -1976,6 +1991,14 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)  	}  zap_buffer: +	/* +	 * This is tricky. Although the buffer is truncated, it may be reused +	 * if blocksize < pagesize and it is attached to the page straddling +	 * EOF. Since the buffer might have been added to BJ_Forget list of the +	 * running transaction, journal_get_write_access() won't clear +	 * b_modified and credit accounting gets confused. So clear b_modified +	 * here. */ +	jh->b_modified = 0;  	journal_put_journal_head(jh);  zap_buffer_no_jh:  	spin_unlock(&journal->j_list_lock); @@ -2024,7 +2047,8 @@ void journal_invalidatepage(journal_t *journal,  		if (offset <= curr_off) {  			/* This block is wholly outside the truncation point */  			lock_buffer(bh); -			may_free &= journal_unmap_buffer(journal, bh); +			may_free &= journal_unmap_buffer(journal, bh, +							 offset > 0);  			unlock_buffer(bh);  		}  		curr_off = next_off; diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index d319963aeb1..c196369fe40 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -896,7 +896,7 @@ static int create_privroot(struct dentry *dentry) { return 0; }  #endif  /* Actual operations that are exported to VFS-land */ -const struct xattr_handler *reiserfs_xattr_handlers[] = { +static const struct xattr_handler *reiserfs_xattr_handlers[] = {  #ifdef CONFIG_REISERFS_FS_XATTR  	&reiserfs_xattr_user_handler,  	&reiserfs_xattr_trusted_handler, diff --git a/fs/udf/file.c b/fs/udf/file.c index d1c6093fd3d..77b5953eaac 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -118,11 +118,20 @@ static int udf_adinicb_write_end(struct file *file,  	return simple_write_end(file, mapping, pos, len, copied, page, fsdata);  } +static ssize_t udf_adinicb_direct_IO(int rw, struct kiocb *iocb, +				     const struct iovec *iov, +				     loff_t offset, unsigned long nr_segs) +{ +	/* Fallback to buffered I/O. */ +	return 0; +} +  const struct address_space_operations udf_adinicb_aops = {  	.readpage	= udf_adinicb_readpage,  	.writepage	= udf_adinicb_writepage,  	.write_begin	= udf_adinicb_write_begin,  	.write_end	= udf_adinicb_write_end, +	.direct_IO	= udf_adinicb_direct_IO,  };  static ssize_t udf_file_aio_write(struct kiocb *iocb, const struct iovec *iov, diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 287ef9f587b..df88b957ccf 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -95,11 +95,33 @@ void udf_evict_inode(struct inode *inode)  	}  } +static void udf_write_failed(struct address_space *mapping, loff_t to) +{ +	struct inode *inode = mapping->host; +	struct udf_inode_info *iinfo = UDF_I(inode); +	loff_t isize = inode->i_size; + +	if (to > isize) { +		truncate_pagecache(inode, to, isize); +		if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { +			down_write(&iinfo->i_data_sem); +			udf_truncate_extents(inode); +			up_write(&iinfo->i_data_sem); +		} +	} +} +  static int udf_writepage(struct page *page, struct writeback_control *wbc)  {  	return block_write_full_page(page, udf_get_block, wbc);  } +static int udf_writepages(struct address_space *mapping, +			struct writeback_control *wbc) +{ +	return mpage_writepages(mapping, wbc, udf_get_block); +} +  static int udf_readpage(struct file *file, struct page *page)  {  	return mpage_readpage(page, udf_get_block); @@ -118,21 +140,24 @@ static int udf_write_begin(struct file *file, struct address_space *mapping,  	int ret;  	ret = block_write_begin(mapping, pos, len, flags, pagep, udf_get_block); -	if (unlikely(ret)) { -		struct inode *inode = mapping->host; -		struct udf_inode_info *iinfo = UDF_I(inode); -		loff_t isize = inode->i_size; +	if (unlikely(ret)) +		udf_write_failed(mapping, pos + len); +	return ret; +} -		if (pos + len > isize) { -			truncate_pagecache(inode, pos + len, isize); -			if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) { -				down_write(&iinfo->i_data_sem); -				udf_truncate_extents(inode); -				up_write(&iinfo->i_data_sem); -			} -		} -	} +static ssize_t udf_direct_IO(int rw, struct kiocb *iocb, +			     const struct iovec *iov, +			     loff_t offset, unsigned long nr_segs) +{ +	struct file *file = iocb->ki_filp; +	struct address_space *mapping = file->f_mapping; +	struct inode *inode = mapping->host; +	ssize_t ret; +	ret = blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, +				  udf_get_block); +	if (unlikely(ret < 0 && (rw & WRITE))) +		udf_write_failed(mapping, offset + iov_length(iov, nr_segs));  	return ret;  } @@ -145,8 +170,10 @@ const struct address_space_operations udf_aops = {  	.readpage	= udf_readpage,  	.readpages	= udf_readpages,  	.writepage	= udf_writepage, -	.write_begin		= udf_write_begin, -	.write_end		= generic_write_end, +	.writepages	= udf_writepages, +	.write_begin	= udf_write_begin, +	.write_end	= generic_write_end, +	.direct_IO	= udf_direct_IO,  	.bmap		= udf_bmap,  };  |