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

xfs: Set up infrastructure for log attribute replay

Currently attributes are modified directly across one or more
transactions. But they are not logged or replayed in the event of an
error. The goal of log attr replay is to enable logging and replaying
of attribute operations using the existing delayed operations
infrastructure. This will later enable the attributes to become part of
larger multi part operations that also must first be recorded to the
log. This is mostly of interest in the scheme of parent pointers which
would need to maintain an attribute containing parent inode information
any time an inode is moved, created, or removed. Parent pointers would
then be of interest to any feature that would need to quickly derive an
inode path from the mount point. Online scrub, nfs lookups and fs grow
or shrink operations are all features that could take advantage of this.

This patch adds two new log item types for setting or removing
attributes as deferred operations. The xfs_attri_log_item will log an
intent to set or remove an attribute. The corresponding
xfs_attrd_log_item holds a reference to the xfs_attri_log_item and is
freed once the transaction is done. Both log items use a generic
xfs_attr_log_format structure that contains the attribute name, value,
flags, inode, and an op_flag that indicates if the operations is a set
or remove.

[dchinner: added extra little bits needed for intent whiteouts]

Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Dave Chinner <david@fromorbit.com>

authored by

Allison Henderson and committed by
Dave Chinner
fd920008 9a39cdab

+648 -6
+1
fs/xfs/Makefile
··· 102 102 xfs_buf_item_recover.o \ 103 103 xfs_dquot_item_recover.o \ 104 104 xfs_extfree_item.o \ 105 + xfs_attr_item.o \ 105 106 xfs_icreate_item.o \ 106 107 xfs_inode_item.o \ 107 108 xfs_inode_item_recover.o \
+39 -3
fs/xfs/libxfs/xfs_attr.c
··· 24 24 #include "xfs_quota.h" 25 25 #include "xfs_trans_space.h" 26 26 #include "xfs_trace.h" 27 + #include "xfs_attr_item.h" 28 + 29 + struct kmem_cache *xfs_attri_cache; 30 + struct kmem_cache *xfs_attrd_cache; 27 31 28 32 /* 29 33 * xfs_attr.c ··· 65 61 struct xfs_da_state **state); 66 62 STATIC int xfs_attr_fillstate(xfs_da_state_t *state); 67 63 STATIC int xfs_attr_refillstate(xfs_da_state_t *state); 68 - STATIC int xfs_attr_set_iter(struct xfs_delattr_context *dac, 69 - struct xfs_buf **leaf_bp); 70 64 STATIC int xfs_attr_node_removename(struct xfs_da_args *args, 71 65 struct xfs_da_state *state); 72 66 ··· 168 166 /* 169 167 * Calculate how many blocks we need for the new attribute, 170 168 */ 171 - STATIC int 169 + int 172 170 xfs_attr_calc_size( 173 171 struct xfs_da_args *args, 174 172 int *local) ··· 840 838 if (args->trans) 841 839 xfs_trans_cancel(args->trans); 842 840 goto out_unlock; 841 + } 842 + 843 + int __init 844 + xfs_attri_init_cache(void) 845 + { 846 + xfs_attri_cache = kmem_cache_create("xfs_attri", 847 + sizeof(struct xfs_attri_log_item), 848 + 0, 0, NULL); 849 + 850 + return xfs_attri_cache != NULL ? 0 : -ENOMEM; 851 + } 852 + 853 + void 854 + xfs_attri_destroy_cache(void) 855 + { 856 + kmem_cache_destroy(xfs_attri_cache); 857 + xfs_attri_cache = NULL; 858 + } 859 + 860 + int __init 861 + xfs_attrd_init_cache(void) 862 + { 863 + xfs_attrd_cache = kmem_cache_create("xfs_attrd", 864 + sizeof(struct xfs_attrd_log_item), 865 + 0, 0, NULL); 866 + 867 + return xfs_attrd_cache != NULL ? 0 : -ENOMEM; 868 + } 869 + 870 + void 871 + xfs_attrd_destroy_cache(void) 872 + { 873 + kmem_cache_destroy(xfs_attrd_cache); 874 + xfs_attrd_cache = NULL; 843 875 } 844 876 845 877 /*========================================================================
+38
fs/xfs/libxfs/xfs_attr.h
··· 28 28 */ 29 29 #define ATTR_MAX_VALUELEN (64*1024) /* max length of a value */ 30 30 31 + static inline bool xfs_has_larp(struct xfs_mount *mp) 32 + { 33 + return false; 34 + } 35 + 31 36 /* 32 37 * Kernel-internal version of the attrlist cursor. 33 38 */ ··· 466 461 struct xfs_delattr_context { 467 462 struct xfs_da_args *da_args; 468 463 464 + /* 465 + * Used by xfs_attr_set to hold a leaf buffer across a transaction roll 466 + */ 467 + struct xfs_buf *leaf_bp; 468 + 469 469 /* Used in xfs_attr_rmtval_set_blk to roll through allocating blocks */ 470 470 struct xfs_bmbt_irec map; 471 471 xfs_dablk_t lblkno; ··· 483 473 unsigned int flags; 484 474 enum xfs_delattr_state dela_state; 485 475 }; 476 + 477 + /* 478 + * List of attrs to commit later. 479 + */ 480 + struct xfs_attr_item { 481 + struct xfs_delattr_context xattri_dac; 482 + 483 + /* 484 + * Indicates if the attr operation is a set or a remove 485 + * XFS_ATTR_OP_FLAGS_{SET,REMOVE} 486 + */ 487 + unsigned int xattri_op_flags; 488 + 489 + /* used to log this item to an intent */ 490 + struct list_head xattri_list; 491 + }; 492 + 486 493 487 494 /*======================================================================== 488 495 * Function prototypes for the kernel. ··· 517 490 int xfs_attr_get(struct xfs_da_args *args); 518 491 int xfs_attr_set(struct xfs_da_args *args); 519 492 int xfs_attr_set_args(struct xfs_da_args *args); 493 + int xfs_attr_set_iter(struct xfs_delattr_context *dac, 494 + struct xfs_buf **leaf_bp); 520 495 int xfs_attr_remove_args(struct xfs_da_args *args); 521 496 int xfs_attr_remove_iter(struct xfs_delattr_context *dac); 522 497 bool xfs_attr_namecheck(const void *name, size_t length); 523 498 void xfs_delattr_context_init(struct xfs_delattr_context *dac, 524 499 struct xfs_da_args *args); 500 + int xfs_attr_calc_size(struct xfs_da_args *args, int *local); 501 + 502 + extern struct kmem_cache *xfs_attri_cache; 503 + extern struct kmem_cache *xfs_attrd_cache; 504 + 505 + int __init xfs_attri_init_cache(void); 506 + void xfs_attri_destroy_cache(void); 507 + int __init xfs_attrd_init_cache(void); 508 + void xfs_attrd_destroy_cache(void); 525 509 526 510 #endif /* __XFS_ATTR_H__ */
+9 -1
fs/xfs/libxfs/xfs_defer.c
··· 23 23 #include "xfs_bmap.h" 24 24 #include "xfs_alloc.h" 25 25 #include "xfs_buf.h" 26 + #include "xfs_attr.h" 26 27 27 28 static struct kmem_cache *xfs_defer_pending_cache; 28 29 ··· 870 869 error = xfs_extfree_intent_init_cache(); 871 870 if (error) 872 871 goto err; 873 - 872 + error = xfs_attri_init_cache(); 873 + if (error) 874 + goto err; 875 + error = xfs_attrd_init_cache(); 876 + if (error) 877 + goto err; 874 878 return 0; 875 879 err: 876 880 xfs_defer_destroy_item_caches(); ··· 886 880 void 887 881 xfs_defer_destroy_item_caches(void) 888 882 { 883 + xfs_attri_destroy_cache(); 884 + xfs_attrd_destroy_cache(); 889 885 xfs_extfree_intent_destroy_cache(); 890 886 xfs_bmap_intent_destroy_cache(); 891 887 xfs_refcount_intent_destroy_cache();
+2
fs/xfs/libxfs/xfs_defer.h
··· 63 63 extern const struct xfs_defer_op_type xfs_rmap_update_defer_type; 64 64 extern const struct xfs_defer_op_type xfs_extent_free_defer_type; 65 65 extern const struct xfs_defer_op_type xfs_agfl_free_defer_type; 66 + extern const struct xfs_defer_op_type xfs_attr_defer_type; 67 + 66 68 67 69 /* 68 70 * Deferred operation item relogging limits.
+42 -2
fs/xfs/libxfs/xfs_log_format.h
··· 113 113 #define XLOG_REG_TYPE_CUD_FORMAT 24 114 114 #define XLOG_REG_TYPE_BUI_FORMAT 25 115 115 #define XLOG_REG_TYPE_BUD_FORMAT 26 116 - #define XLOG_REG_TYPE_MAX 26 116 + #define XLOG_REG_TYPE_ATTRI_FORMAT 27 117 + #define XLOG_REG_TYPE_ATTRD_FORMAT 28 118 + #define XLOG_REG_TYPE_ATTR_NAME 29 119 + #define XLOG_REG_TYPE_ATTR_VALUE 30 120 + #define XLOG_REG_TYPE_MAX 30 121 + 117 122 118 123 /* 119 124 * Flags to log operation header ··· 241 236 #define XFS_LI_CUD 0x1243 242 237 #define XFS_LI_BUI 0x1244 /* bmbt update intent */ 243 238 #define XFS_LI_BUD 0x1245 239 + #define XFS_LI_ATTRI 0x1246 /* attr set/remove intent*/ 240 + #define XFS_LI_ATTRD 0x1247 /* attr set/remove done */ 244 241 245 242 #define XFS_LI_TYPE_DESC \ 246 243 { XFS_LI_EFI, "XFS_LI_EFI" }, \ ··· 258 251 { XFS_LI_CUI, "XFS_LI_CUI" }, \ 259 252 { XFS_LI_CUD, "XFS_LI_CUD" }, \ 260 253 { XFS_LI_BUI, "XFS_LI_BUI" }, \ 261 - { XFS_LI_BUD, "XFS_LI_BUD" } 254 + { XFS_LI_BUD, "XFS_LI_BUD" }, \ 255 + { XFS_LI_ATTRI, "XFS_LI_ATTRI" }, \ 256 + { XFS_LI_ATTRD, "XFS_LI_ATTRD" } 262 257 263 258 /* 264 259 * Inode Log Item Format definitions. ··· 900 891 __be32 icl_isize; /* size of inodes */ 901 892 __be32 icl_length; /* length of extent to initialise */ 902 893 __be32 icl_gen; /* inode generation number to use */ 894 + }; 895 + 896 + /* 897 + * Flags for deferred attribute operations. 898 + * Upper bits are flags, lower byte is type code 899 + */ 900 + #define XFS_ATTR_OP_FLAGS_SET 1 /* Set the attribute */ 901 + #define XFS_ATTR_OP_FLAGS_REMOVE 2 /* Remove the attribute */ 902 + #define XFS_ATTR_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */ 903 + 904 + /* 905 + * This is the structure used to lay out an attr log item in the 906 + * log. 907 + */ 908 + struct xfs_attri_log_format { 909 + uint16_t alfi_type; /* attri log item type */ 910 + uint16_t alfi_size; /* size of this item */ 911 + uint32_t __pad; /* pad to 64 bit aligned */ 912 + uint64_t alfi_id; /* attri identifier */ 913 + uint64_t alfi_ino; /* the inode for this attr operation */ 914 + uint32_t alfi_op_flags; /* marks the op as a set or remove */ 915 + uint32_t alfi_name_len; /* attr name length */ 916 + uint32_t alfi_value_len; /* attr value length */ 917 + uint32_t alfi_attr_flags;/* attr flags */ 918 + }; 919 + 920 + struct xfs_attrd_log_format { 921 + uint16_t alfd_type; /* attrd log item type */ 922 + uint16_t alfd_size; /* size of this item */ 923 + uint32_t __pad; /* pad to 64 bit aligned */ 924 + uint64_t alfd_alf_id; /* id of corresponding attri */ 903 925 }; 904 926 905 927 #endif /* __XFS_LOG_FORMAT_H__ */
+2
fs/xfs/libxfs/xfs_log_recover.h
··· 72 72 extern const struct xlog_recover_item_ops xlog_rud_item_ops; 73 73 extern const struct xlog_recover_item_ops xlog_cui_item_ops; 74 74 extern const struct xlog_recover_item_ops xlog_cud_item_ops; 75 + extern const struct xlog_recover_item_ops xlog_attri_item_ops; 76 + extern const struct xlog_recover_item_ops xlog_attrd_item_ops; 75 77 76 78 /* 77 79 * Macros, structures, prototypes for internal log manager use.
+2
fs/xfs/scrub/common.c
··· 23 23 #include "xfs_rmap_btree.h" 24 24 #include "xfs_log.h" 25 25 #include "xfs_trans_priv.h" 26 + #include "xfs_da_format.h" 27 + #include "xfs_da_btree.h" 26 28 #include "xfs_attr.h" 27 29 #include "xfs_reflink.h" 28 30 #include "xfs_ag.h"
+458
fs/xfs/xfs_attr_item.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Copyright (C) 2022 Oracle. All Rights Reserved. 4 + * Author: Allison Henderson <allison.henderson@oracle.com> 5 + */ 6 + 7 + #include "xfs.h" 8 + #include "xfs_fs.h" 9 + #include "xfs_format.h" 10 + #include "xfs_trans_resv.h" 11 + #include "xfs_shared.h" 12 + #include "xfs_mount.h" 13 + #include "xfs_defer.h" 14 + #include "xfs_log_format.h" 15 + #include "xfs_trans.h" 16 + #include "xfs_trans_priv.h" 17 + #include "xfs_log.h" 18 + #include "xfs_inode.h" 19 + #include "xfs_da_format.h" 20 + #include "xfs_da_btree.h" 21 + #include "xfs_attr.h" 22 + #include "xfs_attr_item.h" 23 + #include "xfs_trace.h" 24 + #include "xfs_inode.h" 25 + #include "xfs_trans_space.h" 26 + #include "xfs_error.h" 27 + #include "xfs_log_priv.h" 28 + #include "xfs_log_recover.h" 29 + 30 + static const struct xfs_item_ops xfs_attri_item_ops; 31 + static const struct xfs_item_ops xfs_attrd_item_ops; 32 + 33 + static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip) 34 + { 35 + return container_of(lip, struct xfs_attri_log_item, attri_item); 36 + } 37 + 38 + STATIC void 39 + xfs_attri_item_free( 40 + struct xfs_attri_log_item *attrip) 41 + { 42 + kmem_free(attrip->attri_item.li_lv_shadow); 43 + kmem_free(attrip); 44 + } 45 + 46 + /* 47 + * Freeing the attrip requires that we remove it from the AIL if it has already 48 + * been placed there. However, the ATTRI may not yet have been placed in the 49 + * AIL when called by xfs_attri_release() from ATTRD processing due to the 50 + * ordering of committed vs unpin operations in bulk insert operations. Hence 51 + * the reference count to ensure only the last caller frees the ATTRI. 52 + */ 53 + STATIC void 54 + xfs_attri_release( 55 + struct xfs_attri_log_item *attrip) 56 + { 57 + ASSERT(atomic_read(&attrip->attri_refcount) > 0); 58 + if (!atomic_dec_and_test(&attrip->attri_refcount)) 59 + return; 60 + 61 + xfs_trans_ail_delete(&attrip->attri_item, 0); 62 + xfs_attri_item_free(attrip); 63 + } 64 + 65 + STATIC void 66 + xfs_attri_item_size( 67 + struct xfs_log_item *lip, 68 + int *nvecs, 69 + int *nbytes) 70 + { 71 + struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 72 + 73 + *nvecs += 2; 74 + *nbytes += sizeof(struct xfs_attri_log_format) + 75 + xlog_calc_iovec_len(attrip->attri_name_len); 76 + 77 + if (!attrip->attri_value_len) 78 + return; 79 + 80 + *nvecs += 1; 81 + *nbytes += xlog_calc_iovec_len(attrip->attri_value_len); 82 + } 83 + 84 + /* 85 + * This is called to fill in the log iovecs for the given attri log 86 + * item. We use 1 iovec for the attri_format_item, 1 for the name, and 87 + * another for the value if it is present 88 + */ 89 + STATIC void 90 + xfs_attri_item_format( 91 + struct xfs_log_item *lip, 92 + struct xfs_log_vec *lv) 93 + { 94 + struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 95 + struct xfs_log_iovec *vecp = NULL; 96 + 97 + attrip->attri_format.alfi_type = XFS_LI_ATTRI; 98 + attrip->attri_format.alfi_size = 1; 99 + 100 + /* 101 + * This size accounting must be done before copying the attrip into the 102 + * iovec. If we do it after, the wrong size will be recorded to the log 103 + * and we trip across assertion checks for bad region sizes later during 104 + * the log recovery. 105 + */ 106 + 107 + ASSERT(attrip->attri_name_len > 0); 108 + attrip->attri_format.alfi_size++; 109 + 110 + if (attrip->attri_value_len > 0) 111 + attrip->attri_format.alfi_size++; 112 + 113 + xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT, 114 + &attrip->attri_format, 115 + sizeof(struct xfs_attri_log_format)); 116 + xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_NAME, 117 + attrip->attri_name, 118 + xlog_calc_iovec_len(attrip->attri_name_len)); 119 + if (attrip->attri_value_len > 0) 120 + xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTR_VALUE, 121 + attrip->attri_value, 122 + xlog_calc_iovec_len(attrip->attri_value_len)); 123 + } 124 + 125 + /* 126 + * The unpin operation is the last place an ATTRI is manipulated in the log. It 127 + * is either inserted in the AIL or aborted in the event of a log I/O error. In 128 + * either case, the ATTRI transaction has been successfully committed to make 129 + * it this far. Therefore, we expect whoever committed the ATTRI to either 130 + * construct and commit the ATTRD or drop the ATTRD's reference in the event of 131 + * error. Simply drop the log's ATTRI reference now that the log is done with 132 + * it. 133 + */ 134 + STATIC void 135 + xfs_attri_item_unpin( 136 + struct xfs_log_item *lip, 137 + int remove) 138 + { 139 + xfs_attri_release(ATTRI_ITEM(lip)); 140 + } 141 + 142 + 143 + STATIC void 144 + xfs_attri_item_release( 145 + struct xfs_log_item *lip) 146 + { 147 + xfs_attri_release(ATTRI_ITEM(lip)); 148 + } 149 + 150 + /* 151 + * Allocate and initialize an attri item. Caller may allocate an additional 152 + * trailing buffer for name and value 153 + */ 154 + STATIC struct xfs_attri_log_item * 155 + xfs_attri_init( 156 + struct xfs_mount *mp, 157 + uint32_t name_len, 158 + uint32_t value_len) 159 + 160 + { 161 + struct xfs_attri_log_item *attrip; 162 + uint32_t name_vec_len = 0; 163 + uint32_t value_vec_len = 0; 164 + uint32_t buffer_size; 165 + 166 + if (name_len) 167 + name_vec_len = xlog_calc_iovec_len(name_len); 168 + if (value_len) 169 + value_vec_len = xlog_calc_iovec_len(value_len); 170 + 171 + buffer_size = name_vec_len + value_vec_len; 172 + 173 + if (buffer_size) { 174 + attrip = kmem_zalloc(sizeof(struct xfs_attri_log_item) + 175 + buffer_size, KM_NOFS); 176 + if (attrip == NULL) 177 + return NULL; 178 + } else { 179 + attrip = kmem_cache_zalloc(xfs_attri_cache, 180 + GFP_NOFS | __GFP_NOFAIL); 181 + } 182 + 183 + attrip->attri_name_len = name_len; 184 + if (name_len) 185 + attrip->attri_name = ((char *)attrip) + 186 + sizeof(struct xfs_attri_log_item); 187 + else 188 + attrip->attri_name = NULL; 189 + 190 + attrip->attri_value_len = value_len; 191 + if (value_len) 192 + attrip->attri_value = ((char *)attrip) + 193 + sizeof(struct xfs_attri_log_item) + 194 + name_vec_len; 195 + else 196 + attrip->attri_value = NULL; 197 + 198 + xfs_log_item_init(mp, &attrip->attri_item, XFS_LI_ATTRI, 199 + &xfs_attri_item_ops); 200 + attrip->attri_format.alfi_id = (uintptr_t)(void *)attrip; 201 + atomic_set(&attrip->attri_refcount, 2); 202 + 203 + return attrip; 204 + } 205 + 206 + /* 207 + * Copy an attr format buffer from the given buf, and into the destination attr 208 + * format structure. 209 + */ 210 + STATIC int 211 + xfs_attri_copy_format( 212 + struct xfs_log_iovec *buf, 213 + struct xfs_attri_log_format *dst_attr_fmt) 214 + { 215 + struct xfs_attri_log_format *src_attr_fmt = buf->i_addr; 216 + size_t len; 217 + 218 + len = sizeof(struct xfs_attri_log_format); 219 + if (buf->i_len != len) { 220 + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL); 221 + return -EFSCORRUPTED; 222 + } 223 + 224 + memcpy((char *)dst_attr_fmt, (char *)src_attr_fmt, len); 225 + return 0; 226 + } 227 + 228 + static inline struct xfs_attrd_log_item *ATTRD_ITEM(struct xfs_log_item *lip) 229 + { 230 + return container_of(lip, struct xfs_attrd_log_item, attrd_item); 231 + } 232 + 233 + STATIC void 234 + xfs_attrd_item_free(struct xfs_attrd_log_item *attrdp) 235 + { 236 + kmem_free(attrdp->attrd_item.li_lv_shadow); 237 + kmem_free(attrdp); 238 + } 239 + 240 + STATIC void 241 + xfs_attrd_item_size( 242 + struct xfs_log_item *lip, 243 + int *nvecs, 244 + int *nbytes) 245 + { 246 + *nvecs += 1; 247 + *nbytes += sizeof(struct xfs_attrd_log_format); 248 + } 249 + 250 + /* 251 + * This is called to fill in the log iovecs for the given attrd log item. We use 252 + * only 1 iovec for the attrd_format, and we point that at the attr_log_format 253 + * structure embedded in the attrd item. 254 + */ 255 + STATIC void 256 + xfs_attrd_item_format( 257 + struct xfs_log_item *lip, 258 + struct xfs_log_vec *lv) 259 + { 260 + struct xfs_attrd_log_item *attrdp = ATTRD_ITEM(lip); 261 + struct xfs_log_iovec *vecp = NULL; 262 + 263 + attrdp->attrd_format.alfd_type = XFS_LI_ATTRD; 264 + attrdp->attrd_format.alfd_size = 1; 265 + 266 + xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRD_FORMAT, 267 + &attrdp->attrd_format, 268 + sizeof(struct xfs_attrd_log_format)); 269 + } 270 + 271 + /* 272 + * The ATTRD is either committed or aborted if the transaction is canceled. If 273 + * the transaction is canceled, drop our reference to the ATTRI and free the 274 + * ATTRD. 275 + */ 276 + STATIC void 277 + xfs_attrd_item_release( 278 + struct xfs_log_item *lip) 279 + { 280 + struct xfs_attrd_log_item *attrdp = ATTRD_ITEM(lip); 281 + 282 + xfs_attri_release(attrdp->attrd_attrip); 283 + xfs_attrd_item_free(attrdp); 284 + } 285 + 286 + STATIC xfs_lsn_t 287 + xfs_attri_item_committed( 288 + struct xfs_log_item *lip, 289 + xfs_lsn_t lsn) 290 + { 291 + struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 292 + 293 + /* 294 + * The attrip refers to xfs_attr_item memory to log the name and value 295 + * with the intent item. This already occurred when the intent was 296 + * committed so these fields are no longer accessed. Clear them out of 297 + * caution since we're about to free the xfs_attr_item. 298 + */ 299 + attrip->attri_name = NULL; 300 + attrip->attri_value = NULL; 301 + 302 + /* 303 + * The ATTRI is logged only once and cannot be moved in the log, so 304 + * simply return the lsn at which it's been logged. 305 + */ 306 + return lsn; 307 + } 308 + 309 + STATIC bool 310 + xfs_attri_item_match( 311 + struct xfs_log_item *lip, 312 + uint64_t intent_id) 313 + { 314 + return ATTRI_ITEM(lip)->attri_format.alfi_id == intent_id; 315 + } 316 + 317 + /* Is this recovered ATTRI format ok? */ 318 + static inline bool 319 + xfs_attri_validate( 320 + struct xfs_mount *mp, 321 + struct xfs_attri_log_format *attrp) 322 + { 323 + unsigned int op = attrp->alfi_op_flags & 324 + XFS_ATTR_OP_FLAGS_TYPE_MASK; 325 + 326 + if (attrp->__pad != 0) 327 + return false; 328 + 329 + /* alfi_op_flags should be either a set or remove */ 330 + if (op != XFS_ATTR_OP_FLAGS_SET && op != XFS_ATTR_OP_FLAGS_REMOVE) 331 + return false; 332 + 333 + if (attrp->alfi_value_len > XATTR_SIZE_MAX) 334 + return false; 335 + 336 + if ((attrp->alfi_name_len > XATTR_NAME_MAX) || 337 + (attrp->alfi_name_len == 0)) 338 + return false; 339 + 340 + return xfs_verify_ino(mp, attrp->alfi_ino); 341 + } 342 + 343 + STATIC int 344 + xlog_recover_attri_commit_pass2( 345 + struct xlog *log, 346 + struct list_head *buffer_list, 347 + struct xlog_recover_item *item, 348 + xfs_lsn_t lsn) 349 + { 350 + int error; 351 + struct xfs_mount *mp = log->l_mp; 352 + struct xfs_attri_log_item *attrip; 353 + struct xfs_attri_log_format *attri_formatp; 354 + int region = 0; 355 + 356 + attri_formatp = item->ri_buf[region].i_addr; 357 + 358 + /* Validate xfs_attri_log_format */ 359 + if (!xfs_attri_validate(mp, attri_formatp)) { 360 + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); 361 + return -EFSCORRUPTED; 362 + } 363 + 364 + /* memory alloc failure will cause replay to abort */ 365 + attrip = xfs_attri_init(mp, attri_formatp->alfi_name_len, 366 + attri_formatp->alfi_value_len); 367 + if (attrip == NULL) 368 + return -ENOMEM; 369 + 370 + error = xfs_attri_copy_format(&item->ri_buf[region], 371 + &attrip->attri_format); 372 + if (error) 373 + goto out; 374 + 375 + region++; 376 + memcpy(attrip->attri_name, item->ri_buf[region].i_addr, 377 + attrip->attri_name_len); 378 + 379 + if (!xfs_attr_namecheck(attrip->attri_name, attrip->attri_name_len)) { 380 + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); 381 + error = -EFSCORRUPTED; 382 + goto out; 383 + } 384 + 385 + if (attrip->attri_value_len > 0) { 386 + region++; 387 + memcpy(attrip->attri_value, item->ri_buf[region].i_addr, 388 + attrip->attri_value_len); 389 + } 390 + 391 + /* 392 + * The ATTRI has two references. One for the ATTRD and one for ATTRI to 393 + * ensure it makes it into the AIL. Insert the ATTRI into the AIL 394 + * directly and drop the ATTRI reference. Note that 395 + * xfs_trans_ail_update() drops the AIL lock. 396 + */ 397 + xfs_trans_ail_insert(log->l_ailp, &attrip->attri_item, lsn); 398 + xfs_attri_release(attrip); 399 + return 0; 400 + out: 401 + xfs_attri_item_free(attrip); 402 + return error; 403 + } 404 + 405 + /* 406 + * This routine is called when an ATTRD format structure is found in a committed 407 + * transaction in the log. Its purpose is to cancel the corresponding ATTRI if 408 + * it was still in the log. To do this it searches the AIL for the ATTRI with 409 + * an id equal to that in the ATTRD format structure. If we find it we drop 410 + * the ATTRD reference, which removes the ATTRI from the AIL and frees it. 411 + */ 412 + STATIC int 413 + xlog_recover_attrd_commit_pass2( 414 + struct xlog *log, 415 + struct list_head *buffer_list, 416 + struct xlog_recover_item *item, 417 + xfs_lsn_t lsn) 418 + { 419 + struct xfs_attrd_log_format *attrd_formatp; 420 + 421 + attrd_formatp = item->ri_buf[0].i_addr; 422 + if (item->ri_buf[0].i_len != sizeof(struct xfs_attrd_log_format)) { 423 + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL); 424 + return -EFSCORRUPTED; 425 + } 426 + 427 + xlog_recover_release_intent(log, XFS_LI_ATTRI, 428 + attrd_formatp->alfd_alf_id); 429 + return 0; 430 + } 431 + 432 + static const struct xfs_item_ops xfs_attri_item_ops = { 433 + .flags = XFS_ITEM_INTENT, 434 + .iop_size = xfs_attri_item_size, 435 + .iop_format = xfs_attri_item_format, 436 + .iop_unpin = xfs_attri_item_unpin, 437 + .iop_committed = xfs_attri_item_committed, 438 + .iop_release = xfs_attri_item_release, 439 + .iop_match = xfs_attri_item_match, 440 + }; 441 + 442 + const struct xlog_recover_item_ops xlog_attri_item_ops = { 443 + .item_type = XFS_LI_ATTRI, 444 + .commit_pass2 = xlog_recover_attri_commit_pass2, 445 + }; 446 + 447 + static const struct xfs_item_ops xfs_attrd_item_ops = { 448 + .flags = XFS_ITEM_RELEASE_WHEN_COMMITTED | 449 + XFS_ITEM_INTENT_DONE, 450 + .iop_size = xfs_attrd_item_size, 451 + .iop_format = xfs_attrd_item_format, 452 + .iop_release = xfs_attrd_item_release, 453 + }; 454 + 455 + const struct xlog_recover_item_ops xlog_attrd_item_ops = { 456 + .item_type = XFS_LI_ATTRD, 457 + .commit_pass2 = xlog_recover_attrd_commit_pass2, 458 + };
+46
fs/xfs/xfs_attr_item.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later 2 + * 3 + * Copyright (C) 2022 Oracle. All Rights Reserved. 4 + * Author: Allison Henderson <allison.henderson@oracle.com> 5 + */ 6 + #ifndef __XFS_ATTR_ITEM_H__ 7 + #define __XFS_ATTR_ITEM_H__ 8 + 9 + /* kernel only ATTRI/ATTRD definitions */ 10 + 11 + struct xfs_mount; 12 + struct kmem_zone; 13 + 14 + /* 15 + * This is the "attr intention" log item. It is used to log the fact that some 16 + * extended attribute operations need to be processed. An operation is 17 + * currently either a set or remove. Set or remove operations are described by 18 + * the xfs_attr_item which may be logged to this intent. 19 + * 20 + * During a normal attr operation, name and value point to the name and value 21 + * fields of the caller's xfs_da_args structure. During a recovery, the name 22 + * and value buffers are copied from the log, and stored in a trailing buffer 23 + * attached to the xfs_attr_item until they are committed. They are freed when 24 + * the xfs_attr_item itself is freed when the work is done. 25 + */ 26 + struct xfs_attri_log_item { 27 + struct xfs_log_item attri_item; 28 + atomic_t attri_refcount; 29 + int attri_name_len; 30 + int attri_value_len; 31 + void *attri_name; 32 + void *attri_value; 33 + struct xfs_attri_log_format attri_format; 34 + }; 35 + 36 + /* 37 + * This is the "attr done" log item. It is used to log the fact that some attrs 38 + * earlier mentioned in an attri item have been freed. 39 + */ 40 + struct xfs_attrd_log_item { 41 + struct xfs_log_item attrd_item; 42 + struct xfs_attri_log_item *attrd_attrip; 43 + struct xfs_attrd_log_format attrd_format; 44 + }; 45 + 46 + #endif /* __XFS_ATTR_ITEM_H__ */
+1
fs/xfs/xfs_attr_list.c
··· 15 15 #include "xfs_inode.h" 16 16 #include "xfs_trans.h" 17 17 #include "xfs_bmap.h" 18 + #include "xfs_da_btree.h" 18 19 #include "xfs_attr.h" 19 20 #include "xfs_attr_sf.h" 20 21 #include "xfs_attr_leaf.h"
+2
fs/xfs/xfs_ioctl32.c
··· 17 17 #include "xfs_itable.h" 18 18 #include "xfs_fsops.h" 19 19 #include "xfs_rtalloc.h" 20 + #include "xfs_da_format.h" 21 + #include "xfs_da_btree.h" 20 22 #include "xfs_attr.h" 21 23 #include "xfs_ioctl.h" 22 24 #include "xfs_ioctl32.h"
+2
fs/xfs/xfs_iops.c
··· 13 13 #include "xfs_inode.h" 14 14 #include "xfs_acl.h" 15 15 #include "xfs_quota.h" 16 + #include "xfs_da_format.h" 17 + #include "xfs_da_btree.h" 16 18 #include "xfs_attr.h" 17 19 #include "xfs_trans.h" 18 20 #include "xfs_trace.h"
+2
fs/xfs/xfs_log_recover.c
··· 1800 1800 &xlog_cud_item_ops, 1801 1801 &xlog_bui_item_ops, 1802 1802 &xlog_bud_item_ops, 1803 + &xlog_attri_item_ops, 1804 + &xlog_attrd_item_ops, 1803 1805 }; 1804 1806 1805 1807 static const struct xlog_recover_item_ops *
+2
fs/xfs/xfs_ondisk.h
··· 132 132 XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format, 56); 133 133 XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat, 20); 134 134 XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header, 16); 135 + XFS_CHECK_STRUCT_SIZE(struct xfs_attri_log_format, 40); 136 + XFS_CHECK_STRUCT_SIZE(struct xfs_attrd_log_format, 16); 135 137 136 138 /* 137 139 * The v5 superblock format extended several v4 header structures with