Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2020-2022, Red Hat, Inc. * All Rights Reserved. */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_log_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_inode.h" #include "xfs_trans.h" #include "xfs_trans_priv.h" #include "xfs_ag.h" #include "xfs_iunlink_item.h" #include "xfs_trace.h" #include "xfs_error.h" struct kmem_cache *xfs_iunlink_cache; static inline struct xfs_iunlink_item *IUL_ITEM(struct xfs_log_item *lip) { return container_of(lip, struct xfs_iunlink_item, item); } static void xfs_iunlink_item_release( struct xfs_log_item *lip) { struct xfs_iunlink_item *iup = IUL_ITEM(lip); xfs_perag_put(iup->pag); kmem_cache_free(xfs_iunlink_cache, IUL_ITEM(lip)); } static uint64_t xfs_iunlink_item_sort( struct xfs_log_item *lip) { return IUL_ITEM(lip)->ip->i_ino; } /* * Look up the inode cluster buffer and log the on-disk unlinked inode change * we need to make. */ static int xfs_iunlink_log_dinode( struct xfs_trans *tp, struct xfs_iunlink_item *iup) { struct xfs_mount *mp = tp->t_mountp; struct xfs_inode *ip = iup->ip; struct xfs_dinode *dip; struct xfs_buf *ibp; int offset; int error; error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp); if (error) return error; /* * Don't log the unlinked field on stale buffers as this may be the * transaction that frees the inode cluster and relogging the buffer * here will incorrectly remove the stale state. */ if (ibp->b_flags & XBF_STALE) goto out; dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset); /* Make sure the old pointer isn't garbage. */ if (be32_to_cpu(dip->di_next_unlinked) != iup->old_agino) { xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, sizeof(*dip), __this_address); error = -EFSCORRUPTED; goto out; } trace_xfs_iunlink_update_dinode(mp, iup->pag->pag_agno, XFS_INO_TO_AGINO(mp, ip->i_ino), be32_to_cpu(dip->di_next_unlinked), iup->next_agino); dip->di_next_unlinked = cpu_to_be32(iup->next_agino); offset = ip->i_imap.im_boffset + offsetof(struct xfs_dinode, di_next_unlinked); xfs_dinode_calc_crc(mp, dip); xfs_trans_inode_buf(tp, ibp); xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1); return 0; out: xfs_trans_brelse(tp, ibp); return error; } /* * On precommit, we grab the inode cluster buffer for the inode number we were * passed, then update the next unlinked field for that inode in the buffer and * log the buffer. This ensures that the inode cluster buffer was logged in the * correct order w.r.t. other inode cluster buffers. We can then remove the * iunlink item from the transaction and release it as it is has now served it's * purpose. */ static int xfs_iunlink_item_precommit( struct xfs_trans *tp, struct xfs_log_item *lip) { struct xfs_iunlink_item *iup = IUL_ITEM(lip); int error; error = xfs_iunlink_log_dinode(tp, iup); list_del(&lip->li_trans); xfs_iunlink_item_release(lip); return error; } static const struct xfs_item_ops xfs_iunlink_item_ops = { .iop_release = xfs_iunlink_item_release, .iop_sort = xfs_iunlink_item_sort, .iop_precommit = xfs_iunlink_item_precommit, }; /* * Initialize the inode log item for a newly allocated (in-core) inode. * * Inode extents can only reside within an AG. Hence specify the starting * block for the inode chunk by offset within an AG as well as the * length of the allocated extent. * * This joins the item to the transaction and marks it dirty so * that we don't need a separate call to do this, nor does the * caller need to know anything about the iunlink item. */ int xfs_iunlink_log_inode( struct xfs_trans *tp, struct xfs_inode *ip, struct xfs_perag *pag, xfs_agino_t next_agino) { struct xfs_mount *mp = tp->t_mountp; struct xfs_iunlink_item *iup; ASSERT(xfs_verify_agino_or_null(pag, next_agino)); ASSERT(xfs_verify_agino_or_null(pag, ip->i_next_unlinked)); /* * Since we're updating a linked list, we should never find that the * current pointer is the same as the new value, unless we're * terminating the list. */ if (ip->i_next_unlinked == next_agino) { if (next_agino != NULLAGINO) return -EFSCORRUPTED; return 0; } iup = kmem_cache_zalloc(xfs_iunlink_cache, GFP_KERNEL | __GFP_NOFAIL); xfs_log_item_init(mp, &iup->item, XFS_LI_IUNLINK, &xfs_iunlink_item_ops); iup->ip = ip; iup->next_agino = next_agino; iup->old_agino = ip->i_next_unlinked; iup->pag = xfs_perag_hold(pag); xfs_trans_add_item(tp, &iup->item); tp->t_flags |= XFS_TRANS_DIRTY; set_bit(XFS_LI_DIRTY, &iup->item.li_flags); return 0; } |