forked from Minki/linux
0860adfdb2
btrfs_commit_transaction has the following loop before we commit the transaction. do { // attempt to do some useful stuff and/or sleep } while (atomic_read(&cur_trans->num_writers) > 1 || (should_grow && cur_trans->num_joined != joined)); This is used to prevent from the TRANS_START to get in the way of a committing transaction. But it does not prevent from TRANS_JOIN, that is we would do this loop for a long time if some writers JOIN the current transaction endlessly. Because we need join the current transaction to do some useful stuff, we can not block TRANS_JOIN here. So we introduce a external writer counter, which is used to count the TRANS_USERSPACE/TRANS_START writers. If the external writer counter is zero, we can break the above loop. In order to make the code more clear, we don't use enum variant to define the type of the transaction handle, use bitmask instead. Signed-off-by: Miao Xie <miaox@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
165 lines
5.6 KiB
C
165 lines
5.6 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_TRANSACTION__
|
|
#define __BTRFS_TRANSACTION__
|
|
#include "btrfs_inode.h"
|
|
#include "delayed-ref.h"
|
|
#include "ctree.h"
|
|
|
|
struct btrfs_transaction {
|
|
u64 transid;
|
|
/*
|
|
* total external writers(USERSPACE/START/ATTACH) in this
|
|
* transaction, it must be zero before the transaction is
|
|
* being committed
|
|
*/
|
|
atomic_t num_extwriters;
|
|
/*
|
|
* total writers in this transaction, it must be zero before the
|
|
* transaction can end
|
|
*/
|
|
atomic_t num_writers;
|
|
atomic_t use_count;
|
|
|
|
unsigned long num_joined;
|
|
|
|
spinlock_t commit_lock;
|
|
int in_commit;
|
|
int commit_done;
|
|
int blocked;
|
|
struct list_head list;
|
|
struct extent_io_tree dirty_pages;
|
|
unsigned long start_time;
|
|
wait_queue_head_t writer_wait;
|
|
wait_queue_head_t commit_wait;
|
|
struct list_head pending_snapshots;
|
|
struct list_head ordered_operations;
|
|
struct btrfs_delayed_ref_root delayed_refs;
|
|
int aborted;
|
|
};
|
|
|
|
#define __TRANS_FREEZABLE (1U << 0)
|
|
|
|
#define __TRANS_USERSPACE (1U << 8)
|
|
#define __TRANS_START (1U << 9)
|
|
#define __TRANS_ATTACH (1U << 10)
|
|
#define __TRANS_JOIN (1U << 11)
|
|
#define __TRANS_JOIN_NOLOCK (1U << 12)
|
|
|
|
#define TRANS_USERSPACE (__TRANS_USERSPACE | __TRANS_FREEZABLE)
|
|
#define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
|
|
#define TRANS_ATTACH (__TRANS_ATTACH)
|
|
#define TRANS_JOIN (__TRANS_JOIN | __TRANS_FREEZABLE)
|
|
#define TRANS_JOIN_NOLOCK (__TRANS_JOIN_NOLOCK)
|
|
|
|
#define TRANS_EXTWRITERS (__TRANS_USERSPACE | __TRANS_START | \
|
|
__TRANS_ATTACH)
|
|
|
|
struct btrfs_trans_handle {
|
|
u64 transid;
|
|
u64 bytes_reserved;
|
|
u64 qgroup_reserved;
|
|
unsigned long use_count;
|
|
unsigned long blocks_reserved;
|
|
unsigned long blocks_used;
|
|
unsigned long delayed_ref_updates;
|
|
struct btrfs_transaction *transaction;
|
|
struct btrfs_block_rsv *block_rsv;
|
|
struct btrfs_block_rsv *orig_rsv;
|
|
short aborted;
|
|
short adding_csums;
|
|
bool allocating_chunk;
|
|
unsigned int type;
|
|
/*
|
|
* this root is only needed to validate that the root passed to
|
|
* start_transaction is the same as the one passed to end_transaction.
|
|
* Subvolume quota depends on this
|
|
*/
|
|
struct btrfs_root *root;
|
|
struct seq_list delayed_ref_elem;
|
|
struct list_head qgroup_ref_list;
|
|
struct list_head new_bgs;
|
|
};
|
|
|
|
struct btrfs_pending_snapshot {
|
|
struct dentry *dentry;
|
|
struct inode *dir;
|
|
struct btrfs_root *root;
|
|
struct btrfs_root *snap;
|
|
struct btrfs_qgroup_inherit *inherit;
|
|
/* block reservation for the operation */
|
|
struct btrfs_block_rsv block_rsv;
|
|
u64 qgroup_reserved;
|
|
/* extra metadata reseration for relocation */
|
|
int error;
|
|
bool readonly;
|
|
struct list_head list;
|
|
};
|
|
|
|
static inline void btrfs_set_inode_last_trans(struct btrfs_trans_handle *trans,
|
|
struct inode *inode)
|
|
{
|
|
BTRFS_I(inode)->last_trans = trans->transaction->transid;
|
|
BTRFS_I(inode)->last_sub_trans = BTRFS_I(inode)->root->log_transid;
|
|
BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->root->last_log_commit;
|
|
}
|
|
|
|
int btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
|
|
int num_items);
|
|
struct btrfs_trans_handle *btrfs_start_transaction_lflush(
|
|
struct btrfs_root *root, int num_items);
|
|
struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root);
|
|
struct btrfs_trans_handle *btrfs_join_transaction_nolock(struct btrfs_root *root);
|
|
struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root);
|
|
struct btrfs_trans_handle *btrfs_attach_transaction_barrier(
|
|
struct btrfs_root *root);
|
|
struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *root);
|
|
int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid);
|
|
int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
|
|
int btrfs_add_dead_root(struct btrfs_root *root);
|
|
int btrfs_defrag_root(struct btrfs_root *root);
|
|
int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root);
|
|
int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
int wait_for_unblock);
|
|
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
int btrfs_end_transaction_dmeta(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
void btrfs_throttle(struct btrfs_root *root);
|
|
int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root);
|
|
int btrfs_write_and_wait_marked_extents(struct btrfs_root *root,
|
|
struct extent_io_tree *dirty_pages, int mark);
|
|
int btrfs_write_marked_extents(struct btrfs_root *root,
|
|
struct extent_io_tree *dirty_pages, int mark);
|
|
int btrfs_wait_marked_extents(struct btrfs_root *root,
|
|
struct extent_io_tree *dirty_pages, int mark);
|
|
int btrfs_transaction_blocked(struct btrfs_fs_info *info);
|
|
int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
|
|
#endif
|