@@ -36,6 +36,7 @@ struct inode {
uint32_t i_gid;
uint32_t i_nlink;
xfs_dev_t i_rdev; /* This actually holds xfs_dev_t */
+ unsigned int i_count;
unsigned long i_state; /* Not actually used in userspace */
uint32_t i_generation;
uint64_t i_version;
@@ -61,6 +62,11 @@ static inline void i_gid_write(struct inode *inode, uint32_t gid)
inode->i_gid = gid;
}
+static inline void ihold(struct inode *inode)
+{
+ inode->i_count++;
+}
+
typedef struct xfs_inode {
struct cache_node i_node;
struct xfs_mount *i_mount; /* fs mount struct ptr */
@@ -123,6 +123,7 @@
#define xfs_inode_validate_extsize libxfs_inode_validate_extsize
#define xfs_iread_extents libxfs_iread_extents
+#define xfs_irele libxfs_irele
#define xfs_log_calc_minimum_size libxfs_log_calc_minimum_size
#define xfs_log_get_max_trans_res libxfs_log_get_max_trans_res
#define xfs_log_sb libxfs_log_sb
@@ -1254,6 +1254,7 @@ libxfs_iget(
if (!ip)
return -ENOMEM;
+ VFS_I(ip)->i_count = 1;
ip->i_ino = ino;
ip->i_mount = mp;
error = xfs_imap(mp, tp, ip->i_ino, &ip->i_imap, 0);
@@ -1305,9 +1306,13 @@ void
libxfs_irele(
struct xfs_inode *ip)
{
- ASSERT(ip->i_itemp == NULL);
- libxfs_idestroy(ip);
- kmem_cache_free(xfs_inode_zone, ip);
+ VFS_I(ip)->i_count--;
+
+ if (VFS_I(ip)->i_count == 0) {
+ ASSERT(ip->i_itemp == NULL);
+ libxfs_idestroy(ip);
+ kmem_cache_free(xfs_inode_zone, ip);
+ }
}
/*
@@ -551,10 +551,14 @@ xfs_defer_move(
* deferred ops state is transferred to the capture structure and the
* transaction is then ready for the caller to commit it. If there are no
* intent items to capture, this function returns NULL.
+ *
+ * If capture_ip is not NULL, the capture structure will obtain an extra
+ * reference to the inode.
*/
static struct xfs_defer_capture *
xfs_defer_ops_capture(
- struct xfs_trans *tp)
+ struct xfs_trans *tp,
+ struct xfs_inode *capture_ip)
{
struct xfs_defer_capture *dfc;
@@ -580,6 +584,15 @@ xfs_defer_ops_capture(
/* Preserve the log reservation size. */
dfc->dfc_logres = tp->t_log_res;
+ /*
+ * Grab an extra reference to this inode and attach it to the capture
+ * structure.
+ */
+ if (capture_ip) {
+ ihold(VFS_I(capture_ip));
+ dfc->dfc_capture_ip = capture_ip;
+ }
+
return dfc;
}
@@ -590,24 +603,33 @@ xfs_defer_ops_release(
struct xfs_defer_capture *dfc)
{
xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
+ if (dfc->dfc_capture_ip)
+ xfs_irele(dfc->dfc_capture_ip);
kmem_free(dfc);
}
/*
* Capture any deferred ops and commit the transaction. This is the last step
- * needed to finish a log intent item that we recovered from the log.
+ * needed to finish a log intent item that we recovered from the log. If any
+ * of the deferred ops operate on an inode, the caller must pass in that inode
+ * so that the reference can be transferred to the capture structure. The
+ * caller must hold ILOCK_EXCL on the inode, and must unlock it before calling
+ * xfs_defer_ops_continue.
*/
int
xfs_defer_ops_capture_and_commit(
struct xfs_trans *tp,
+ struct xfs_inode *capture_ip,
struct list_head *capture_list)
{
struct xfs_mount *mp = tp->t_mountp;
struct xfs_defer_capture *dfc;
int error;
+ ASSERT(!capture_ip || xfs_isilocked(capture_ip, XFS_ILOCK_EXCL));
+
/* If we don't capture anything, commit transaction and exit. */
- dfc = xfs_defer_ops_capture(tp);
+ dfc = xfs_defer_ops_capture(tp, capture_ip);
if (!dfc)
return xfs_trans_commit(tp);
@@ -624,16 +646,26 @@ xfs_defer_ops_capture_and_commit(
/*
* Attach a chain of captured deferred ops to a new transaction and free the
- * capture structure.
+ * capture structure. If an inode was captured, it will be passed back to the
+ * caller with ILOCK_EXCL held and joined to the transaction with lockflags==0.
+ * The caller now owns the inode reference.
*/
void
xfs_defer_ops_continue(
struct xfs_defer_capture *dfc,
- struct xfs_trans *tp)
+ struct xfs_trans *tp,
+ struct xfs_inode **captured_ipp)
{
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
+ /* Lock and join the captured inode to the new transaction. */
+ if (dfc->dfc_capture_ip) {
+ xfs_ilock(dfc->dfc_capture_ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, dfc->dfc_capture_ip, 0);
+ }
+ *captured_ipp = dfc->dfc_capture_ip;
+
/* Move captured dfops chain and state to the transaction. */
list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);
tp->t_flags |= dfc->dfc_tpflags;
@@ -82,6 +82,12 @@ struct xfs_defer_capture {
/* Log reservation saved from the transaction. */
unsigned int dfc_logres;
+
+ /*
+ * An inode reference that must be maintained to complete the deferred
+ * work.
+ */
+ struct xfs_inode *dfc_capture_ip;
};
/*
@@ -89,8 +95,9 @@ struct xfs_defer_capture {
* 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_inode *capture_ip, struct list_head *capture_list);
+void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp,
+ struct xfs_inode **captured_ipp);
void xfs_defer_ops_release(struct xfs_mount *mp, struct xfs_defer_capture *d);
#endif /* __XFS_DEFER_H__ */