mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 11:51:27 +00:00
03f7767c9f
One thing I never quite got around to doing is porting the log intent item recovery code to reconstruct the deferred pending work state. As a result, each intent item open codes xfs_defer_finish_one in its recovery method, because that's what the EFI code did before xfs_defer.c even existed. This is a gross thing to have left unfixed -- if an EFI cannot proceed due to busy extents, we end up creating separate new EFIs for each unfinished work item, which is a change in behavior from what runtime would have done. Worse yet, Long Li pointed out that there's a UAF in the recovery code. The ->commit_pass2 function adds the intent item to the AIL and drops the refcount. The one remaining refcount is now owned by the recovery mechanism (aka the log intent items in the AIL) with the intent of giving the refcount to the intent done item in the ->iop_recover function. However, if something fails later in recovery, xlog_recover_finish will walk the recovered intent items in the AIL and release them. If the CIL hasn't been pushed before that point (which is possible since we don't force the log until later) then the intent done release will try to free its associated intent, which has already been freed. This patch starts to address this mess by having the ->commit_pass2 functions recreate the xfs_defer_pending state. The next few patches will fix the recovery functions. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
137 lines
4.3 KiB
C
137 lines
4.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0+ */
|
|
/*
|
|
* Copyright (C) 2016 Oracle. All Rights Reserved.
|
|
* Author: Darrick J. Wong <darrick.wong@oracle.com>
|
|
*/
|
|
#ifndef __XFS_DEFER_H__
|
|
#define __XFS_DEFER_H__
|
|
|
|
struct xfs_btree_cur;
|
|
struct xfs_defer_op_type;
|
|
struct xfs_defer_capture;
|
|
|
|
/*
|
|
* Header for deferred operation list.
|
|
*/
|
|
enum xfs_defer_ops_type {
|
|
XFS_DEFER_OPS_TYPE_BMAP,
|
|
XFS_DEFER_OPS_TYPE_REFCOUNT,
|
|
XFS_DEFER_OPS_TYPE_RMAP,
|
|
XFS_DEFER_OPS_TYPE_FREE,
|
|
XFS_DEFER_OPS_TYPE_AGFL_FREE,
|
|
XFS_DEFER_OPS_TYPE_ATTR,
|
|
XFS_DEFER_OPS_TYPE_MAX,
|
|
};
|
|
|
|
/*
|
|
* Save a log intent item and a list of extents, so that we can replay
|
|
* whatever action had to happen to the extent list and file the log done
|
|
* item.
|
|
*/
|
|
struct xfs_defer_pending {
|
|
struct list_head dfp_list; /* pending items */
|
|
struct list_head dfp_work; /* work items */
|
|
struct xfs_log_item *dfp_intent; /* log intent item */
|
|
struct xfs_log_item *dfp_done; /* log done item */
|
|
unsigned int dfp_count; /* # extent items */
|
|
enum xfs_defer_ops_type dfp_type;
|
|
};
|
|
|
|
void xfs_defer_add(struct xfs_trans *tp, enum xfs_defer_ops_type type,
|
|
struct list_head *h);
|
|
int xfs_defer_finish_noroll(struct xfs_trans **tp);
|
|
int xfs_defer_finish(struct xfs_trans **tp);
|
|
void xfs_defer_cancel(struct xfs_trans *);
|
|
void xfs_defer_move(struct xfs_trans *dtp, struct xfs_trans *stp);
|
|
|
|
/* Description of a deferred type. */
|
|
struct xfs_defer_op_type {
|
|
struct xfs_log_item *(*create_intent)(struct xfs_trans *tp,
|
|
struct list_head *items, unsigned int count, bool sort);
|
|
void (*abort_intent)(struct xfs_log_item *intent);
|
|
struct xfs_log_item *(*create_done)(struct xfs_trans *tp,
|
|
struct xfs_log_item *intent, unsigned int count);
|
|
int (*finish_item)(struct xfs_trans *tp, struct xfs_log_item *done,
|
|
struct list_head *item, struct xfs_btree_cur **state);
|
|
void (*finish_cleanup)(struct xfs_trans *tp,
|
|
struct xfs_btree_cur *state, int error);
|
|
void (*cancel_item)(struct list_head *item);
|
|
unsigned int max_items;
|
|
};
|
|
|
|
extern const struct xfs_defer_op_type xfs_bmap_update_defer_type;
|
|
extern const struct xfs_defer_op_type xfs_refcount_update_defer_type;
|
|
extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
|
|
extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
|
|
extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
|
|
extern const struct xfs_defer_op_type xfs_attr_defer_type;
|
|
|
|
|
|
/*
|
|
* Deferred operation item relogging limits.
|
|
*/
|
|
#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
|
|
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
|
|
|
|
/* Resources that must be held across a transaction roll. */
|
|
struct xfs_defer_resources {
|
|
/* held buffers */
|
|
struct xfs_buf *dr_bp[XFS_DEFER_OPS_NR_BUFS];
|
|
|
|
/* inodes with no unlock flags */
|
|
struct xfs_inode *dr_ip[XFS_DEFER_OPS_NR_INODES];
|
|
|
|
/* number of held buffers */
|
|
unsigned short dr_bufs;
|
|
|
|
/* bitmap of ordered buffers */
|
|
unsigned short dr_ordered;
|
|
|
|
/* number of held inodes */
|
|
unsigned short dr_inos;
|
|
};
|
|
|
|
/*
|
|
* This structure enables a dfops user to detach the chain of deferred
|
|
* operations from a transaction so that they can be continued later.
|
|
*/
|
|
struct xfs_defer_capture {
|
|
/* List of other capture structures. */
|
|
struct list_head dfc_list;
|
|
|
|
/* Deferred ops state saved from the transaction. */
|
|
struct list_head dfc_dfops;
|
|
unsigned int dfc_tpflags;
|
|
|
|
/* Block reservations for the data and rt devices. */
|
|
unsigned int dfc_blkres;
|
|
unsigned int dfc_rtxres;
|
|
|
|
/* Log reservation saved from the transaction. */
|
|
unsigned int dfc_logres;
|
|
|
|
struct xfs_defer_resources dfc_held;
|
|
};
|
|
|
|
/*
|
|
* Functions to capture a chain of deferred operations and continue them later.
|
|
* This doesn't normally happen except log recovery.
|
|
*/
|
|
int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp,
|
|
struct list_head *capture_list);
|
|
void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp,
|
|
struct xfs_defer_resources *dres);
|
|
void xfs_defer_ops_capture_abort(struct xfs_mount *mp,
|
|
struct xfs_defer_capture *d);
|
|
void xfs_defer_resources_rele(struct xfs_defer_resources *dres);
|
|
|
|
void xfs_defer_start_recovery(struct xfs_log_item *lip,
|
|
enum xfs_defer_ops_type dfp_type, struct list_head *r_dfops);
|
|
void xfs_defer_cancel_recovery(struct xfs_mount *mp,
|
|
struct xfs_defer_pending *dfp);
|
|
|
|
int __init xfs_defer_init_item_caches(void);
|
|
void xfs_defer_destroy_item_caches(void);
|
|
|
|
#endif /* __XFS_DEFER_H__ */
|