mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 08:02:07 +00:00
656f30dba7
While we have a transaction ongoing, the VM might decide at any time to call btree_inode->i_mapping->a_ops->writepages(), which will start writeback of dirty pages belonging to btree nodes/leafs. This call might return an error or the writeback might finish with an error before we attempt to commit the running transaction. If this happens, we might have no way of knowing that such error happened when we are committing the transaction - because the pages might no longer be marked dirty nor tagged for writeback (if a subsequent modification to the extent buffer didn't happen before the transaction commit) which makes filemap_fdata[write|wait]_range unable to find such pages (even if they're marked with SetPageError). So if this happens we must abort the transaction, otherwise we commit a super block with btree roots that point to btree nodes/leafs whose content on disk is invalid - either garbage or the content of some node/leaf from a past generation that got cowed or deleted and is no longer valid (for this later case we end up getting error messages like "parent transid verify failed on 10826481664 wanted 25748 found 29562" when reading btree nodes/leafs from disk). Note that setting and checking AS_EIO/AS_ENOSPC in the btree inode's i_mapping would not be enough because we need to distinguish between log tree extents (not fatal) vs non-log tree extents (fatal) and because the next call to filemap_fdatawait_range() will catch and clear such errors in the mapping - and that call might be from a log sync and not from a transaction commit, which means we would not know about the error at transaction commit time. Also, checking for the eb flag EXTENT_BUFFER_IOERR at transaction commit time isn't done and would not be completely reliable, as the eb might be removed from memory and read back when trying to get it, which clears that flag right before reading the eb's pages from disk, making us not know about the previous write error. Using the new 3 flags for the btree inode also makes us achieve the goal of AS_EIO/AS_ENOSPC when writepages() returns success, started writeback for all dirty pages and before filemap_fdatawait_range() is called, the writeback for all dirty pages had already finished with errors - because we were not using AS_EIO/AS_ENOSPC, filemap_fdatawait_range() would return success, as it could not know that writeback errors happened (the pages were no longer tagged for writeback). Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
309 lines
8.1 KiB
C
309 lines
8.1 KiB
C
/*
|
|
* Copyright (C) 2007 Oracle. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License v2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 021110-1307, USA.
|
|
*/
|
|
|
|
#ifndef __BTRFS_I__
|
|
#define __BTRFS_I__
|
|
|
|
#include <linux/hash.h>
|
|
#include "extent_map.h"
|
|
#include "extent_io.h"
|
|
#include "ordered-data.h"
|
|
#include "delayed-inode.h"
|
|
|
|
/*
|
|
* ordered_data_close is set by truncate when a file that used
|
|
* to have good data has been truncated to zero. When it is set
|
|
* the btrfs file release call will add this inode to the
|
|
* ordered operations list so that we make sure to flush out any
|
|
* new data the application may have written before commit.
|
|
*/
|
|
#define BTRFS_INODE_ORDERED_DATA_CLOSE 0
|
|
#define BTRFS_INODE_ORPHAN_META_RESERVED 1
|
|
#define BTRFS_INODE_DUMMY 2
|
|
#define BTRFS_INODE_IN_DEFRAG 3
|
|
#define BTRFS_INODE_DELALLOC_META_RESERVED 4
|
|
#define BTRFS_INODE_HAS_ORPHAN_ITEM 5
|
|
#define BTRFS_INODE_HAS_ASYNC_EXTENT 6
|
|
#define BTRFS_INODE_NEEDS_FULL_SYNC 7
|
|
#define BTRFS_INODE_COPY_EVERYTHING 8
|
|
#define BTRFS_INODE_IN_DELALLOC_LIST 9
|
|
#define BTRFS_INODE_READDIO_NEED_LOCK 10
|
|
#define BTRFS_INODE_HAS_PROPS 11
|
|
/*
|
|
* The following 3 bits are meant only for the btree inode.
|
|
* When any of them is set, it means an error happened while writing an
|
|
* extent buffer belonging to:
|
|
* 1) a non-log btree
|
|
* 2) a log btree and first log sub-transaction
|
|
* 3) a log btree and second log sub-transaction
|
|
*/
|
|
#define BTRFS_INODE_BTREE_ERR 12
|
|
#define BTRFS_INODE_BTREE_LOG1_ERR 13
|
|
#define BTRFS_INODE_BTREE_LOG2_ERR 14
|
|
|
|
/* in memory btrfs inode */
|
|
struct btrfs_inode {
|
|
/* which subvolume this inode belongs to */
|
|
struct btrfs_root *root;
|
|
|
|
/* key used to find this inode on disk. This is used by the code
|
|
* to read in roots of subvolumes
|
|
*/
|
|
struct btrfs_key location;
|
|
|
|
/* Lock for counters */
|
|
spinlock_t lock;
|
|
|
|
/* the extent_tree has caches of all the extent mappings to disk */
|
|
struct extent_map_tree extent_tree;
|
|
|
|
/* the io_tree does range state (DIRTY, LOCKED etc) */
|
|
struct extent_io_tree io_tree;
|
|
|
|
/* special utility tree used to record which mirrors have already been
|
|
* tried when checksums fail for a given block
|
|
*/
|
|
struct extent_io_tree io_failure_tree;
|
|
|
|
/* held while logging the inode in tree-log.c */
|
|
struct mutex log_mutex;
|
|
|
|
/* held while doing delalloc reservations */
|
|
struct mutex delalloc_mutex;
|
|
|
|
/* used to order data wrt metadata */
|
|
struct btrfs_ordered_inode_tree ordered_tree;
|
|
|
|
/* list of all the delalloc inodes in the FS. There are times we need
|
|
* to write all the delalloc pages to disk, and this list is used
|
|
* to walk them all.
|
|
*/
|
|
struct list_head delalloc_inodes;
|
|
|
|
/* node for the red-black tree that links inodes in subvolume root */
|
|
struct rb_node rb_node;
|
|
|
|
unsigned long runtime_flags;
|
|
|
|
/* Keep track of who's O_SYNC/fsyncing currently */
|
|
atomic_t sync_writers;
|
|
|
|
/* full 64 bit generation number, struct vfs_inode doesn't have a big
|
|
* enough field for this.
|
|
*/
|
|
u64 generation;
|
|
|
|
/*
|
|
* transid of the trans_handle that last modified this inode
|
|
*/
|
|
u64 last_trans;
|
|
|
|
/*
|
|
* transid that last logged this inode
|
|
*/
|
|
u64 logged_trans;
|
|
|
|
/*
|
|
* log transid when this inode was last modified
|
|
*/
|
|
int last_sub_trans;
|
|
|
|
/* a local copy of root's last_log_commit */
|
|
int last_log_commit;
|
|
|
|
/* total number of bytes pending delalloc, used by stat to calc the
|
|
* real block usage of the file
|
|
*/
|
|
u64 delalloc_bytes;
|
|
|
|
/*
|
|
* total number of bytes pending defrag, used by stat to check whether
|
|
* it needs COW.
|
|
*/
|
|
u64 defrag_bytes;
|
|
|
|
/*
|
|
* the size of the file stored in the metadata on disk. data=ordered
|
|
* means the in-memory i_size might be larger than the size on disk
|
|
* because not all the blocks are written yet.
|
|
*/
|
|
u64 disk_i_size;
|
|
|
|
/*
|
|
* if this is a directory then index_cnt is the counter for the index
|
|
* number for new files that are created
|
|
*/
|
|
u64 index_cnt;
|
|
|
|
/* Cache the directory index number to speed the dir/file remove */
|
|
u64 dir_index;
|
|
|
|
/* the fsync log has some corner cases that mean we have to check
|
|
* directories to see if any unlinks have been done before
|
|
* the directory was logged. See tree-log.c for all the
|
|
* details
|
|
*/
|
|
u64 last_unlink_trans;
|
|
|
|
/*
|
|
* Number of bytes outstanding that are going to need csums. This is
|
|
* used in ENOSPC accounting.
|
|
*/
|
|
u64 csum_bytes;
|
|
|
|
/* flags field from the on disk inode */
|
|
u32 flags;
|
|
|
|
/*
|
|
* Counters to keep track of the number of extent item's we may use due
|
|
* to delalloc and such. outstanding_extents is the number of extent
|
|
* items we think we'll end up using, and reserved_extents is the number
|
|
* of extent items we've reserved metadata for.
|
|
*/
|
|
unsigned outstanding_extents;
|
|
unsigned reserved_extents;
|
|
|
|
/*
|
|
* always compress this one file
|
|
*/
|
|
unsigned force_compress;
|
|
|
|
struct btrfs_delayed_node *delayed_node;
|
|
|
|
struct inode vfs_inode;
|
|
};
|
|
|
|
extern unsigned char btrfs_filetype_table[];
|
|
|
|
static inline struct btrfs_inode *BTRFS_I(struct inode *inode)
|
|
{
|
|
return container_of(inode, struct btrfs_inode, vfs_inode);
|
|
}
|
|
|
|
static inline unsigned long btrfs_inode_hash(u64 objectid,
|
|
const struct btrfs_root *root)
|
|
{
|
|
u64 h = objectid ^ (root->objectid * GOLDEN_RATIO_PRIME);
|
|
|
|
#if BITS_PER_LONG == 32
|
|
h = (h >> 32) ^ (h & 0xffffffff);
|
|
#endif
|
|
|
|
return (unsigned long)h;
|
|
}
|
|
|
|
static inline void btrfs_insert_inode_hash(struct inode *inode)
|
|
{
|
|
unsigned long h = btrfs_inode_hash(inode->i_ino, BTRFS_I(inode)->root);
|
|
|
|
__insert_inode_hash(inode, h);
|
|
}
|
|
|
|
static inline u64 btrfs_ino(struct inode *inode)
|
|
{
|
|
u64 ino = BTRFS_I(inode)->location.objectid;
|
|
|
|
/*
|
|
* !ino: btree_inode
|
|
* type == BTRFS_ROOT_ITEM_KEY: subvol dir
|
|
*/
|
|
if (!ino || BTRFS_I(inode)->location.type == BTRFS_ROOT_ITEM_KEY)
|
|
ino = inode->i_ino;
|
|
return ino;
|
|
}
|
|
|
|
static inline void btrfs_i_size_write(struct inode *inode, u64 size)
|
|
{
|
|
i_size_write(inode, size);
|
|
BTRFS_I(inode)->disk_i_size = size;
|
|
}
|
|
|
|
static inline bool btrfs_is_free_space_inode(struct inode *inode)
|
|
{
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
|
|
if (root == root->fs_info->tree_root &&
|
|
btrfs_ino(inode) != BTRFS_BTREE_INODE_OBJECTID)
|
|
return true;
|
|
if (BTRFS_I(inode)->location.objectid == BTRFS_FREE_INO_OBJECTID)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static inline int btrfs_inode_in_log(struct inode *inode, u64 generation)
|
|
{
|
|
if (BTRFS_I(inode)->logged_trans == generation &&
|
|
BTRFS_I(inode)->last_sub_trans <=
|
|
BTRFS_I(inode)->last_log_commit &&
|
|
BTRFS_I(inode)->last_sub_trans <=
|
|
BTRFS_I(inode)->root->last_log_commit)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
#define BTRFS_DIO_ORIG_BIO_SUBMITTED 0x1
|
|
|
|
struct btrfs_dio_private {
|
|
struct inode *inode;
|
|
unsigned long flags;
|
|
u64 logical_offset;
|
|
u64 disk_bytenr;
|
|
u64 bytes;
|
|
void *private;
|
|
|
|
/* number of bios pending for this dio */
|
|
atomic_t pending_bios;
|
|
|
|
/* IO errors */
|
|
int errors;
|
|
|
|
/* orig_bio is our btrfs_io_bio */
|
|
struct bio *orig_bio;
|
|
|
|
/* dio_bio came from fs/direct-io.c */
|
|
struct bio *dio_bio;
|
|
|
|
/*
|
|
* The original bio may be splited to several sub-bios, this is
|
|
* done during endio of sub-bios
|
|
*/
|
|
int (*subio_endio)(struct inode *, struct btrfs_io_bio *, int);
|
|
};
|
|
|
|
/*
|
|
* Disable DIO read nolock optimization, so new dio readers will be forced
|
|
* to grab i_mutex. It is used to avoid the endless truncate due to
|
|
* nonlocked dio read.
|
|
*/
|
|
static inline void btrfs_inode_block_unlocked_dio(struct inode *inode)
|
|
{
|
|
set_bit(BTRFS_INODE_READDIO_NEED_LOCK, &BTRFS_I(inode)->runtime_flags);
|
|
smp_mb();
|
|
}
|
|
|
|
static inline void btrfs_inode_resume_unlocked_dio(struct inode *inode)
|
|
{
|
|
smp_mb__before_atomic();
|
|
clear_bit(BTRFS_INODE_READDIO_NEED_LOCK,
|
|
&BTRFS_I(inode)->runtime_flags);
|
|
}
|
|
|
|
bool btrfs_page_exists_in_range(struct inode *inode, loff_t start, loff_t end);
|
|
|
|
#endif
|