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

xfs: Implement attr logging and replay

This patch adds the needed routines to create, log and recover logged
extended attribute intents.

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
1d08e11d fd920008

+378 -1
+1
fs/xfs/libxfs/xfs_defer.c
··· 186 186 [XFS_DEFER_OPS_TYPE_RMAP] = &xfs_rmap_update_defer_type, 187 187 [XFS_DEFER_OPS_TYPE_FREE] = &xfs_extent_free_defer_type, 188 188 [XFS_DEFER_OPS_TYPE_AGFL_FREE] = &xfs_agfl_free_defer_type, 189 + [XFS_DEFER_OPS_TYPE_ATTR] = &xfs_attr_defer_type, 189 190 }; 190 191 191 192 static bool
+1
fs/xfs/libxfs/xfs_defer.h
··· 19 19 XFS_DEFER_OPS_TYPE_RMAP, 20 20 XFS_DEFER_OPS_TYPE_FREE, 21 21 XFS_DEFER_OPS_TYPE_AGFL_FREE, 22 + XFS_DEFER_OPS_TYPE_ATTR, 22 23 XFS_DEFER_OPS_TYPE_MAX, 23 24 }; 24 25
+8 -1
fs/xfs/libxfs/xfs_format.h
··· 390 390 return (sbp->sb_features_incompat & feature) != 0; 391 391 } 392 392 393 - #define XFS_SB_FEAT_INCOMPAT_LOG_ALL 0 393 + #define XFS_SB_FEAT_INCOMPAT_LOG_XATTRS (1 << 0) /* Delayed Attributes */ 394 + #define XFS_SB_FEAT_INCOMPAT_LOG_ALL \ 395 + (XFS_SB_FEAT_INCOMPAT_LOG_XATTRS) 394 396 #define XFS_SB_FEAT_INCOMPAT_LOG_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_LOG_ALL 395 397 static inline bool 396 398 xfs_sb_has_incompat_log_feature( ··· 417 415 sbp->sb_features_log_incompat |= features; 418 416 } 419 417 418 + static inline bool xfs_sb_version_haslogxattrs(struct xfs_sb *sbp) 419 + { 420 + return xfs_sb_is_v5(sbp) && (sbp->sb_features_log_incompat & 421 + XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); 422 + } 420 423 421 424 static inline bool 422 425 xfs_is_quota_inode(struct xfs_sb *sbp, xfs_ino_t ino)
+368
fs/xfs/xfs_attr_item.c
··· 13 13 #include "xfs_defer.h" 14 14 #include "xfs_log_format.h" 15 15 #include "xfs_trans.h" 16 + #include "xfs_bmap_btree.h" 16 17 #include "xfs_trans_priv.h" 17 18 #include "xfs_log.h" 18 19 #include "xfs_inode.h" ··· 30 29 31 30 static const struct xfs_item_ops xfs_attri_item_ops; 32 31 static const struct xfs_item_ops xfs_attrd_item_ops; 32 + static struct xfs_attrd_log_item *xfs_trans_get_attrd(struct xfs_trans *tp, 33 + struct xfs_attri_log_item *attrip); 33 34 34 35 static inline struct xfs_attri_log_item *ATTRI_ITEM(struct xfs_log_item *lip) 35 36 { ··· 286 283 xfs_attrd_item_free(attrdp); 287 284 } 288 285 286 + static struct xfs_log_item * 287 + xfs_attrd_item_intent( 288 + struct xfs_log_item *lip) 289 + { 290 + return &ATTRD_ITEM(lip)->attrd_attrip->attri_item; 291 + } 292 + 293 + /* 294 + * Performs one step of an attribute update intent and marks the attrd item 295 + * dirty.. An attr operation may be a set or a remove. Note that the 296 + * transaction is marked dirty regardless of whether the operation succeeds or 297 + * fails to support the ATTRI/ATTRD lifecycle rules. 298 + */ 299 + STATIC int 300 + xfs_xattri_finish_update( 301 + struct xfs_delattr_context *dac, 302 + struct xfs_attrd_log_item *attrdp, 303 + struct xfs_buf **leaf_bp, 304 + uint32_t op_flags) 305 + { 306 + struct xfs_da_args *args = dac->da_args; 307 + unsigned int op = op_flags & 308 + XFS_ATTR_OP_FLAGS_TYPE_MASK; 309 + int error; 310 + 311 + switch (op) { 312 + case XFS_ATTR_OP_FLAGS_SET: 313 + error = xfs_attr_set_iter(dac, leaf_bp); 314 + break; 315 + case XFS_ATTR_OP_FLAGS_REMOVE: 316 + ASSERT(XFS_IFORK_Q(args->dp)); 317 + error = xfs_attr_remove_iter(dac); 318 + break; 319 + default: 320 + error = -EFSCORRUPTED; 321 + break; 322 + } 323 + 324 + /* 325 + * Mark the transaction dirty, even on error. This ensures the 326 + * transaction is aborted, which: 327 + * 328 + * 1.) releases the ATTRI and frees the ATTRD 329 + * 2.) shuts down the filesystem 330 + */ 331 + args->trans->t_flags |= XFS_TRANS_DIRTY | XFS_TRANS_HAS_INTENT_DONE; 332 + 333 + /* 334 + * attr intent/done items are null when logged attributes are disabled 335 + */ 336 + if (attrdp) 337 + set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags); 338 + 339 + return error; 340 + } 341 + 342 + /* Log an attr to the intent item. */ 343 + STATIC void 344 + xfs_attr_log_item( 345 + struct xfs_trans *tp, 346 + struct xfs_attri_log_item *attrip, 347 + struct xfs_attr_item *attr) 348 + { 349 + struct xfs_attri_log_format *attrp; 350 + 351 + tp->t_flags |= XFS_TRANS_DIRTY; 352 + set_bit(XFS_LI_DIRTY, &attrip->attri_item.li_flags); 353 + 354 + /* 355 + * At this point the xfs_attr_item has been constructed, and we've 356 + * created the log intent. Fill in the attri log item and log format 357 + * structure with fields from this xfs_attr_item 358 + */ 359 + attrp = &attrip->attri_format; 360 + attrp->alfi_ino = attr->xattri_dac.da_args->dp->i_ino; 361 + attrp->alfi_op_flags = attr->xattri_op_flags; 362 + attrp->alfi_value_len = attr->xattri_dac.da_args->valuelen; 363 + attrp->alfi_name_len = attr->xattri_dac.da_args->namelen; 364 + attrp->alfi_attr_flags = attr->xattri_dac.da_args->attr_filter; 365 + 366 + memcpy(attrip->attri_name, attr->xattri_dac.da_args->name, 367 + attr->xattri_dac.da_args->namelen); 368 + memcpy(attrip->attri_value, attr->xattri_dac.da_args->value, 369 + attr->xattri_dac.da_args->valuelen); 370 + attrip->attri_name_len = attr->xattri_dac.da_args->namelen; 371 + attrip->attri_value_len = attr->xattri_dac.da_args->valuelen; 372 + } 373 + 374 + /* Get an ATTRI. */ 375 + static struct xfs_log_item * 376 + xfs_attr_create_intent( 377 + struct xfs_trans *tp, 378 + struct list_head *items, 379 + unsigned int count, 380 + bool sort) 381 + { 382 + struct xfs_mount *mp = tp->t_mountp; 383 + struct xfs_attri_log_item *attrip; 384 + struct xfs_attr_item *attr; 385 + 386 + ASSERT(count == 1); 387 + 388 + if (!xfs_sb_version_haslogxattrs(&mp->m_sb)) 389 + return NULL; 390 + 391 + /* 392 + * Each attr item only performs one attribute operation at a time, so 393 + * this is a list of one 394 + */ 395 + list_for_each_entry(attr, items, xattri_list) { 396 + attrip = xfs_attri_init(mp, attr->xattri_dac.da_args->namelen, 397 + attr->xattri_dac.da_args->valuelen); 398 + if (attrip == NULL) 399 + return NULL; 400 + 401 + xfs_trans_add_item(tp, &attrip->attri_item); 402 + xfs_attr_log_item(tp, attrip, attr); 403 + } 404 + 405 + return &attrip->attri_item; 406 + } 407 + 408 + /* Process an attr. */ 409 + STATIC int 410 + xfs_attr_finish_item( 411 + struct xfs_trans *tp, 412 + struct xfs_log_item *done, 413 + struct list_head *item, 414 + struct xfs_btree_cur **state) 415 + { 416 + struct xfs_attr_item *attr; 417 + struct xfs_attrd_log_item *done_item = NULL; 418 + int error; 419 + struct xfs_delattr_context *dac; 420 + 421 + attr = container_of(item, struct xfs_attr_item, xattri_list); 422 + dac = &attr->xattri_dac; 423 + if (done) 424 + done_item = ATTRD_ITEM(done); 425 + 426 + /* 427 + * Always reset trans after EAGAIN cycle 428 + * since the transaction is new 429 + */ 430 + dac->da_args->trans = tp; 431 + 432 + error = xfs_xattri_finish_update(dac, done_item, &dac->leaf_bp, 433 + attr->xattri_op_flags); 434 + if (error != -EAGAIN) 435 + kmem_free(attr); 436 + 437 + return error; 438 + } 439 + 440 + /* Abort all pending ATTRs. */ 441 + STATIC void 442 + xfs_attr_abort_intent( 443 + struct xfs_log_item *intent) 444 + { 445 + xfs_attri_release(ATTRI_ITEM(intent)); 446 + } 447 + 448 + /* Cancel an attr */ 449 + STATIC void 450 + xfs_attr_cancel_item( 451 + struct list_head *item) 452 + { 453 + struct xfs_attr_item *attr; 454 + 455 + attr = container_of(item, struct xfs_attr_item, xattri_list); 456 + kmem_free(attr); 457 + } 458 + 289 459 STATIC xfs_lsn_t 290 460 xfs_attri_item_committed( 291 461 struct xfs_log_item *lip, ··· 514 338 return false; 515 339 516 340 return xfs_verify_ino(mp, attrp->alfi_ino); 341 + } 342 + 343 + /* 344 + * Process an attr intent item that was recovered from the log. We need to 345 + * delete the attr that it describes. 346 + */ 347 + STATIC int 348 + xfs_attri_item_recover( 349 + struct xfs_log_item *lip, 350 + struct list_head *capture_list) 351 + { 352 + struct xfs_attri_log_item *attrip = ATTRI_ITEM(lip); 353 + struct xfs_attr_item *attr; 354 + struct xfs_mount *mp = lip->li_log->l_mp; 355 + struct xfs_inode *ip; 356 + struct xfs_da_args *args; 357 + struct xfs_trans *tp; 358 + struct xfs_trans_res tres; 359 + struct xfs_attri_log_format *attrp; 360 + int error, ret = 0; 361 + int total; 362 + int local; 363 + struct xfs_attrd_log_item *done_item = NULL; 364 + 365 + /* 366 + * First check the validity of the attr described by the ATTRI. If any 367 + * are bad, then assume that all are bad and just toss the ATTRI. 368 + */ 369 + attrp = &attrip->attri_format; 370 + if (!xfs_attri_validate(mp, attrp) || 371 + !xfs_attr_namecheck(attrip->attri_name, attrip->attri_name_len)) 372 + return -EFSCORRUPTED; 373 + 374 + error = xlog_recover_iget(mp, attrp->alfi_ino, &ip); 375 + if (error) 376 + return error; 377 + 378 + attr = kmem_zalloc(sizeof(struct xfs_attr_item) + 379 + sizeof(struct xfs_da_args), KM_NOFS); 380 + args = (struct xfs_da_args *)(attr + 1); 381 + 382 + attr->xattri_dac.da_args = args; 383 + attr->xattri_op_flags = attrp->alfi_op_flags; 384 + 385 + args->dp = ip; 386 + args->geo = mp->m_attr_geo; 387 + args->whichfork = XFS_ATTR_FORK; 388 + args->name = attrip->attri_name; 389 + args->namelen = attrp->alfi_name_len; 390 + args->hashval = xfs_da_hashname(args->name, args->namelen); 391 + args->attr_filter = attrp->alfi_attr_flags; 392 + 393 + if (attrp->alfi_op_flags == XFS_ATTR_OP_FLAGS_SET) { 394 + args->value = attrip->attri_value; 395 + args->valuelen = attrp->alfi_value_len; 396 + args->total = xfs_attr_calc_size(args, &local); 397 + 398 + tres.tr_logres = M_RES(mp)->tr_attrsetm.tr_logres + 399 + M_RES(mp)->tr_attrsetrt.tr_logres * 400 + args->total; 401 + tres.tr_logcount = XFS_ATTRSET_LOG_COUNT; 402 + tres.tr_logflags = XFS_TRANS_PERM_LOG_RES; 403 + total = args->total; 404 + } else { 405 + tres = M_RES(mp)->tr_attrrm; 406 + total = XFS_ATTRRM_SPACE_RES(mp); 407 + } 408 + error = xfs_trans_alloc(mp, &tres, total, 0, XFS_TRANS_RESERVE, &tp); 409 + if (error) 410 + goto out; 411 + 412 + args->trans = tp; 413 + done_item = xfs_trans_get_attrd(tp, attrip); 414 + 415 + xfs_ilock(ip, XFS_ILOCK_EXCL); 416 + xfs_trans_ijoin(tp, ip, 0); 417 + 418 + ret = xfs_xattri_finish_update(&attr->xattri_dac, done_item, 419 + &attr->xattri_dac.leaf_bp, 420 + attrp->alfi_op_flags); 421 + if (ret == -EAGAIN) { 422 + /* There's more work to do, so add it to this transaction */ 423 + xfs_defer_add(tp, XFS_DEFER_OPS_TYPE_ATTR, &attr->xattri_list); 424 + } else 425 + error = ret; 426 + 427 + if (error) { 428 + xfs_trans_cancel(tp); 429 + goto out_unlock; 430 + } 431 + 432 + error = xfs_defer_ops_capture_and_commit(tp, capture_list); 433 + 434 + out_unlock: 435 + if (attr->xattri_dac.leaf_bp) 436 + xfs_buf_relse(attr->xattri_dac.leaf_bp); 437 + 438 + xfs_iunlock(ip, XFS_ILOCK_EXCL); 439 + xfs_irele(ip); 440 + out: 441 + if (ret != -EAGAIN) 442 + kmem_free(attr); 443 + return error; 444 + } 445 + 446 + /* Re-log an intent item to push the log tail forward. */ 447 + static struct xfs_log_item * 448 + xfs_attri_item_relog( 449 + struct xfs_log_item *intent, 450 + struct xfs_trans *tp) 451 + { 452 + struct xfs_attrd_log_item *attrdp; 453 + struct xfs_attri_log_item *old_attrip; 454 + struct xfs_attri_log_item *new_attrip; 455 + struct xfs_attri_log_format *new_attrp; 456 + struct xfs_attri_log_format *old_attrp; 457 + 458 + old_attrip = ATTRI_ITEM(intent); 459 + old_attrp = &old_attrip->attri_format; 460 + 461 + tp->t_flags |= XFS_TRANS_DIRTY; 462 + attrdp = xfs_trans_get_attrd(tp, old_attrip); 463 + set_bit(XFS_LI_DIRTY, &attrdp->attrd_item.li_flags); 464 + 465 + new_attrip = xfs_attri_init(tp->t_mountp, old_attrp->alfi_name_len, 466 + old_attrp->alfi_value_len); 467 + new_attrp = &new_attrip->attri_format; 468 + 469 + new_attrp->alfi_ino = old_attrp->alfi_ino; 470 + new_attrp->alfi_op_flags = old_attrp->alfi_op_flags; 471 + new_attrp->alfi_value_len = old_attrp->alfi_value_len; 472 + new_attrp->alfi_name_len = old_attrp->alfi_name_len; 473 + new_attrp->alfi_attr_flags = old_attrp->alfi_attr_flags; 474 + 475 + memcpy(new_attrip->attri_name, old_attrip->attri_name, 476 + new_attrip->attri_name_len); 477 + 478 + if (new_attrip->attri_value_len > 0) 479 + memcpy(new_attrip->attri_value, old_attrip->attri_value, 480 + new_attrip->attri_value_len); 481 + 482 + xfs_trans_add_item(tp, &new_attrip->attri_item); 483 + set_bit(XFS_LI_DIRTY, &new_attrip->attri_item.li_flags); 484 + 485 + return &new_attrip->attri_item; 517 486 } 518 487 519 488 STATIC int ··· 724 403 } 725 404 726 405 /* 406 + * This routine is called to allocate an "attr free done" log item. 407 + */ 408 + static struct xfs_attrd_log_item * 409 + xfs_trans_get_attrd(struct xfs_trans *tp, 410 + struct xfs_attri_log_item *attrip) 411 + { 412 + struct xfs_attrd_log_item *attrdp; 413 + 414 + ASSERT(tp != NULL); 415 + 416 + attrdp = kmem_cache_alloc(xfs_attrd_cache, GFP_NOFS | __GFP_NOFAIL); 417 + 418 + xfs_log_item_init(tp->t_mountp, &attrdp->attrd_item, XFS_LI_ATTRD, 419 + &xfs_attrd_item_ops); 420 + attrdp->attrd_attrip = attrip; 421 + attrdp->attrd_format.alfd_alf_id = attrip->attri_format.alfi_id; 422 + 423 + xfs_trans_add_item(tp, &attrdp->attrd_item); 424 + return attrdp; 425 + } 426 + 427 + /* Get an ATTRD so we can process all the attrs. */ 428 + static struct xfs_log_item * 429 + xfs_attr_create_done( 430 + struct xfs_trans *tp, 431 + struct xfs_log_item *intent, 432 + unsigned int count) 433 + { 434 + if (!intent) 435 + return NULL; 436 + 437 + return &xfs_trans_get_attrd(tp, ATTRI_ITEM(intent))->attrd_item; 438 + } 439 + 440 + const struct xfs_defer_op_type xfs_attr_defer_type = { 441 + .max_items = 1, 442 + .create_intent = xfs_attr_create_intent, 443 + .abort_intent = xfs_attr_abort_intent, 444 + .create_done = xfs_attr_create_done, 445 + .finish_item = xfs_attr_finish_item, 446 + .cancel_item = xfs_attr_cancel_item, 447 + }; 448 + 449 + /* 727 450 * This routine is called when an ATTRD format structure is found in a committed 728 451 * transaction in the log. Its purpose is to cancel the corresponding ATTRI if 729 452 * it was still in the log. To do this it searches the AIL for the ATTRI with ··· 801 436 .iop_unpin = xfs_attri_item_unpin, 802 437 .iop_committed = xfs_attri_item_committed, 803 438 .iop_release = xfs_attri_item_release, 439 + .iop_recover = xfs_attri_item_recover, 804 440 .iop_match = xfs_attri_item_match, 441 + .iop_relog = xfs_attri_item_relog, 805 442 }; 806 443 807 444 const struct xlog_recover_item_ops xlog_attri_item_ops = { ··· 817 450 .iop_size = xfs_attrd_item_size, 818 451 .iop_format = xfs_attrd_item_format, 819 452 .iop_release = xfs_attrd_item_release, 453 + .iop_intent = xfs_attrd_item_intent, 820 454 }; 821 455 822 456 const struct xlog_recover_item_ops xlog_attrd_item_ops = {