Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfs: split iop_unlock

The iop_unlock method is called when comitting or cancelling a
transaction. In the latter case, the transaction may or may not be
aborted. While there is no known problem with the current code in
practice, this implementation is limited in that any log item
implementation that might want to differentiate between a commit and a
cancellation must rely on the aborted state. The aborted bit is only
set when the cancelled transaction is dirty, however. This means that
there is no way to distinguish between a commit and a clean transaction
cancellation.

For example, intent log items currently rely on this distinction. The
log item is either transferred to the CIL on commit or released on
transaction cancel. There is currently no possibility for a clean intent
log item in a transaction, but if that state is ever introduced a cancel
of such a transaction will immediately result in memory leaks of the
associated log item(s). This is an interface deficiency and landmine.

To clean this up, replace the iop_unlock method with an iop_release
method that is specific to transaction cancel. The existing
iop_committing method occurs at the same time as iop_unlock in the
commit path and there is no need for two separate callbacks here.
Overload the iop_committing method with the current commit time
iop_unlock implementations to eliminate the need for the latter and
further simplify the interface.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>

authored by

Christoph Hellwig and committed by
Darrick J. Wong
ddf92053 195cd83d

+67 -73
+7 -10
fs/xfs/xfs_bmap_item.c
··· 120 120 * constructed and thus we free the BUI here directly. 121 121 */ 122 122 STATIC void 123 - xfs_bui_item_unlock( 123 + xfs_bui_item_release( 124 124 struct xfs_log_item *lip) 125 125 { 126 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) 127 - xfs_bui_release(BUI_ITEM(lip)); 126 + xfs_bui_release(BUI_ITEM(lip)); 128 127 } 129 128 130 129 /* ··· 133 134 .iop_size = xfs_bui_item_size, 134 135 .iop_format = xfs_bui_item_format, 135 136 .iop_unpin = xfs_bui_item_unpin, 136 - .iop_unlock = xfs_bui_item_unlock, 137 + .iop_release = xfs_bui_item_release, 137 138 }; 138 139 139 140 /* ··· 200 201 * BUD. 201 202 */ 202 203 STATIC void 203 - xfs_bud_item_unlock( 204 + xfs_bud_item_release( 204 205 struct xfs_log_item *lip) 205 206 { 206 207 struct xfs_bud_log_item *budp = BUD_ITEM(lip); 207 208 208 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) { 209 - xfs_bui_release(budp->bud_buip); 210 - kmem_zone_free(xfs_bud_zone, budp); 211 - } 209 + xfs_bui_release(budp->bud_buip); 210 + kmem_zone_free(xfs_bud_zone, budp); 212 211 } 213 212 214 213 /* ··· 240 243 static const struct xfs_item_ops xfs_bud_item_ops = { 241 244 .iop_size = xfs_bud_item_size, 242 245 .iop_format = xfs_bud_item_format, 243 - .iop_unlock = xfs_bud_item_unlock, 246 + .iop_release = xfs_bud_item_release, 244 247 .iop_committed = xfs_bud_item_committed, 245 248 }; 246 249
+12 -3
fs/xfs/xfs_buf_item.c
··· 595 595 * free the item. 596 596 */ 597 597 STATIC void 598 - xfs_buf_item_unlock( 598 + xfs_buf_item_release( 599 599 struct xfs_log_item *lip) 600 600 { 601 601 struct xfs_buf_log_item *bip = BUF_ITEM(lip); ··· 610 610 &lip->li_flags); 611 611 #endif 612 612 613 - trace_xfs_buf_item_unlock(bip); 613 + trace_xfs_buf_item_release(bip); 614 614 615 615 /* 616 616 * The bli dirty state should match whether the blf has logged segments ··· 638 638 return; 639 639 ASSERT(!stale || aborted); 640 640 xfs_buf_relse(bp); 641 + } 642 + 643 + STATIC void 644 + xfs_buf_item_committing( 645 + struct xfs_log_item *lip, 646 + xfs_lsn_t commit_lsn) 647 + { 648 + return xfs_buf_item_release(lip); 641 649 } 642 650 643 651 /* ··· 688 680 .iop_format = xfs_buf_item_format, 689 681 .iop_pin = xfs_buf_item_pin, 690 682 .iop_unpin = xfs_buf_item_unpin, 691 - .iop_unlock = xfs_buf_item_unlock, 683 + .iop_release = xfs_buf_item_release, 684 + .iop_committing = xfs_buf_item_committing, 692 685 .iop_committed = xfs_buf_item_committed, 693 686 .iop_push = xfs_buf_item_push, 694 687 };
+11 -8
fs/xfs/xfs_dquot_item.c
··· 198 198 return rval; 199 199 } 200 200 201 - /* 202 - * Unlock the dquot associated with the log item. 203 - * Clear the fields of the dquot and dquot log item that 204 - * are specific to the current transaction. If the 205 - * hold flags is set, do not unlock the dquot. 206 - */ 207 201 STATIC void 208 - xfs_qm_dquot_logitem_unlock( 202 + xfs_qm_dquot_logitem_release( 209 203 struct xfs_log_item *lip) 210 204 { 211 205 struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; ··· 215 221 xfs_dqunlock(dqp); 216 222 } 217 223 224 + STATIC void 225 + xfs_qm_dquot_logitem_committing( 226 + struct xfs_log_item *lip, 227 + xfs_lsn_t commit_lsn) 228 + { 229 + return xfs_qm_dquot_logitem_release(lip); 230 + } 231 + 218 232 /* 219 233 * This is the ops vector for dquots 220 234 */ ··· 231 229 .iop_format = xfs_qm_dquot_logitem_format, 232 230 .iop_pin = xfs_qm_dquot_logitem_pin, 233 231 .iop_unpin = xfs_qm_dquot_logitem_unpin, 234 - .iop_unlock = xfs_qm_dquot_logitem_unlock, 232 + .iop_release = xfs_qm_dquot_logitem_release, 233 + .iop_committing = xfs_qm_dquot_logitem_committing, 235 234 .iop_push = xfs_qm_dquot_logitem_push, 236 235 .iop_error = xfs_dquot_item_error 237 236 };
+7 -10
fs/xfs/xfs_extfree_item.c
··· 130 130 * constructed and thus we free the EFI here directly. 131 131 */ 132 132 STATIC void 133 - xfs_efi_item_unlock( 133 + xfs_efi_item_release( 134 134 struct xfs_log_item *lip) 135 135 { 136 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) 137 - xfs_efi_release(EFI_ITEM(lip)); 136 + xfs_efi_release(EFI_ITEM(lip)); 138 137 } 139 138 140 139 /* ··· 143 144 .iop_size = xfs_efi_item_size, 144 145 .iop_format = xfs_efi_item_format, 145 146 .iop_unpin = xfs_efi_item_unpin, 146 - .iop_unlock = xfs_efi_item_unlock, 147 + .iop_release = xfs_efi_item_release, 147 148 }; 148 149 149 150 ··· 299 300 * the transaction is cancelled, drop our reference to the EFI and free the EFD. 300 301 */ 301 302 STATIC void 302 - xfs_efd_item_unlock( 303 + xfs_efd_item_release( 303 304 struct xfs_log_item *lip) 304 305 { 305 306 struct xfs_efd_log_item *efdp = EFD_ITEM(lip); 306 307 307 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) { 308 - xfs_efi_release(efdp->efd_efip); 309 - xfs_efd_item_free(efdp); 310 - } 308 + xfs_efi_release(efdp->efd_efip); 309 + xfs_efd_item_free(efdp); 311 310 } 312 311 313 312 /* ··· 339 342 static const struct xfs_item_ops xfs_efd_item_ops = { 340 343 .iop_size = xfs_efd_item_size, 341 344 .iop_format = xfs_efd_item_format, 342 - .iop_unlock = xfs_efd_item_unlock, 345 + .iop_release = xfs_efd_item_release, 343 346 .iop_committed = xfs_efd_item_committed, 344 347 }; 345 348
+3 -7
fs/xfs/xfs_icreate_item.c
··· 57 57 } 58 58 59 59 STATIC void 60 - xfs_icreate_item_unlock( 60 + xfs_icreate_item_release( 61 61 struct xfs_log_item *lip) 62 62 { 63 - struct xfs_icreate_item *icp = ICR_ITEM(lip); 64 - 65 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) 66 - kmem_zone_free(xfs_icreate_zone, icp); 67 - return; 63 + kmem_zone_free(xfs_icreate_zone, ICR_ITEM(lip)); 68 64 } 69 65 70 66 /* ··· 85 89 static const struct xfs_item_ops xfs_icreate_item_ops = { 86 90 .iop_size = xfs_icreate_item_size, 87 91 .iop_format = xfs_icreate_item_format, 88 - .iop_unlock = xfs_icreate_item_unlock, 92 + .iop_release = xfs_icreate_item_release, 89 93 .iop_committed = xfs_icreate_item_committed, 90 94 }; 91 95
+6 -5
fs/xfs/xfs_inode_item.c
··· 566 566 * Unlock the inode associated with the inode log item. 567 567 */ 568 568 STATIC void 569 - xfs_inode_item_unlock( 569 + xfs_inode_item_release( 570 570 struct xfs_log_item *lip) 571 571 { 572 572 struct xfs_inode_log_item *iip = INODE_ITEM(lip); ··· 622 622 STATIC void 623 623 xfs_inode_item_committing( 624 624 struct xfs_log_item *lip, 625 - xfs_lsn_t lsn) 625 + xfs_lsn_t commit_lsn) 626 626 { 627 - INODE_ITEM(lip)->ili_last_lsn = lsn; 627 + INODE_ITEM(lip)->ili_last_lsn = commit_lsn; 628 + return xfs_inode_item_release(lip); 628 629 } 629 630 630 631 /* ··· 636 635 .iop_format = xfs_inode_item_format, 637 636 .iop_pin = xfs_inode_item_pin, 638 637 .iop_unpin = xfs_inode_item_unpin, 639 - .iop_unlock = xfs_inode_item_unlock, 638 + .iop_release = xfs_inode_item_release, 640 639 .iop_committed = xfs_inode_item_committed, 641 640 .iop_push = xfs_inode_item_push, 642 - .iop_committing = xfs_inode_item_committing, 641 + .iop_committing = xfs_inode_item_committing, 643 642 .iop_error = xfs_inode_item_error 644 643 }; 645 644
-2
fs/xfs/xfs_log_cil.c
··· 1024 1024 xfs_trans_del_item(lip); 1025 1025 if (lip->li_ops->iop_committing) 1026 1026 lip->li_ops->iop_committing(lip, xc_commit_lsn); 1027 - if (lip->li_ops->iop_unlock) 1028 - lip->li_ops->iop_unlock(lip); 1029 1027 } 1030 1028 xlog_cil_push_background(log); 1031 1029
+7 -10
fs/xfs/xfs_refcount_item.c
··· 118 118 * constructed and thus we free the CUI here directly. 119 119 */ 120 120 STATIC void 121 - xfs_cui_item_unlock( 121 + xfs_cui_item_release( 122 122 struct xfs_log_item *lip) 123 123 { 124 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) 125 - xfs_cui_release(CUI_ITEM(lip)); 124 + xfs_cui_release(CUI_ITEM(lip)); 126 125 } 127 126 128 127 /* ··· 131 132 .iop_size = xfs_cui_item_size, 132 133 .iop_format = xfs_cui_item_format, 133 134 .iop_unpin = xfs_cui_item_unpin, 134 - .iop_unlock = xfs_cui_item_unlock, 135 + .iop_release = xfs_cui_item_release, 135 136 }; 136 137 137 138 /* ··· 204 205 * CUD. 205 206 */ 206 207 STATIC void 207 - xfs_cud_item_unlock( 208 + xfs_cud_item_release( 208 209 struct xfs_log_item *lip) 209 210 { 210 211 struct xfs_cud_log_item *cudp = CUD_ITEM(lip); 211 212 212 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) { 213 - xfs_cui_release(cudp->cud_cuip); 214 - kmem_zone_free(xfs_cud_zone, cudp); 215 - } 213 + xfs_cui_release(cudp->cud_cuip); 214 + kmem_zone_free(xfs_cud_zone, cudp); 216 215 } 217 216 218 217 /* ··· 244 247 static const struct xfs_item_ops xfs_cud_item_ops = { 245 248 .iop_size = xfs_cud_item_size, 246 249 .iop_format = xfs_cud_item_format, 247 - .iop_unlock = xfs_cud_item_unlock, 250 + .iop_release = xfs_cud_item_release, 248 251 .iop_committed = xfs_cud_item_committed, 249 252 }; 250 253
+7 -10
fs/xfs/xfs_rmap_item.c
··· 117 117 * constructed and thus we free the RUI here directly. 118 118 */ 119 119 STATIC void 120 - xfs_rui_item_unlock( 120 + xfs_rui_item_release( 121 121 struct xfs_log_item *lip) 122 122 { 123 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) 124 - xfs_rui_release(RUI_ITEM(lip)); 123 + xfs_rui_release(RUI_ITEM(lip)); 125 124 } 126 125 127 126 /* ··· 130 131 .iop_size = xfs_rui_item_size, 131 132 .iop_format = xfs_rui_item_format, 132 133 .iop_unpin = xfs_rui_item_unpin, 133 - .iop_unlock = xfs_rui_item_unlock, 134 + .iop_release = xfs_rui_item_release, 134 135 }; 135 136 136 137 /* ··· 225 226 * RUD. 226 227 */ 227 228 STATIC void 228 - xfs_rud_item_unlock( 229 + xfs_rud_item_release( 229 230 struct xfs_log_item *lip) 230 231 { 231 232 struct xfs_rud_log_item *rudp = RUD_ITEM(lip); 232 233 233 - if (test_bit(XFS_LI_ABORTED, &lip->li_flags)) { 234 - xfs_rui_release(rudp->rud_ruip); 235 - kmem_zone_free(xfs_rud_zone, rudp); 236 - } 234 + xfs_rui_release(rudp->rud_ruip); 235 + kmem_zone_free(xfs_rud_zone, rudp); 237 236 } 238 237 239 238 /* ··· 265 268 static const struct xfs_item_ops xfs_rud_item_ops = { 266 269 .iop_size = xfs_rud_item_size, 267 270 .iop_format = xfs_rud_item_format, 268 - .iop_unlock = xfs_rud_item_unlock, 271 + .iop_release = xfs_rud_item_release, 269 272 .iop_committed = xfs_rud_item_committed, 270 273 }; 271 274
+1 -1
fs/xfs/xfs_trace.h
··· 475 475 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_pin); 476 476 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin); 477 477 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unpin_stale); 478 - DEFINE_BUF_ITEM_EVENT(xfs_buf_item_unlock); 478 + DEFINE_BUF_ITEM_EVENT(xfs_buf_item_release); 479 479 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_committed); 480 480 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_push); 481 481 DEFINE_BUF_ITEM_EVENT(xfs_trans_get_buf);
+3 -4
fs/xfs/xfs_trans.c
··· 780 780 xfs_trans_del_item(lip); 781 781 if (abort) 782 782 set_bit(XFS_LI_ABORTED, &lip->li_flags); 783 - 784 - if (lip->li_ops->iop_unlock) 785 - lip->li_ops->iop_unlock(lip); 783 + if (lip->li_ops->iop_release) 784 + lip->li_ops->iop_release(lip); 786 785 } 787 786 } 788 787 ··· 814 815 * 815 816 * If we are called with the aborted flag set, it is because a log write during 816 817 * a CIL checkpoint commit has failed. In this case, all the items in the 817 - * checkpoint have already gone through iop_committed and iop_unlock, which 818 + * checkpoint have already gone through iop_committed and iop_committing, which 818 819 * means that checkpoint commit abort handling is treated exactly the same 819 820 * as an iclog write error even though we haven't started any IO yet. Hence in 820 821 * this case all we need to do is iop_committed processing, followed by an
+2 -2
fs/xfs/xfs_trans.h
··· 72 72 void (*iop_pin)(xfs_log_item_t *); 73 73 void (*iop_unpin)(xfs_log_item_t *, int remove); 74 74 uint (*iop_push)(struct xfs_log_item *, struct list_head *); 75 - void (*iop_unlock)(xfs_log_item_t *); 75 + void (*iop_committing)(struct xfs_log_item *, xfs_lsn_t commit_lsn); 76 + void (*iop_release)(struct xfs_log_item *); 76 77 xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t); 77 - void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t); 78 78 void (*iop_error)(xfs_log_item_t *, xfs_buf_t *); 79 79 }; 80 80
+1 -1
fs/xfs/xfs_trans_buf.c
··· 427 427 428 428 /* 429 429 * Mark the buffer as not needing to be unlocked when the buf item's 430 - * iop_unlock() routine is called. The buffer must already be locked 430 + * iop_committing() routine is called. The buffer must already be locked 431 431 * and associated with the given transaction. 432 432 */ 433 433 /* ARGSUSED */