From: Sasha Levin Date: Mon, 26 Aug 2019 17:12:58 +0000 (-0400) Subject: XFS fixes for 4.19 X-Git-Tag: v4.14.141~21 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1f6a2e9ab0f0a43556f917e7e1beddb00c6a2063;p=thirdparty%2Fkernel%2Fstable-queue.git XFS fixes for 4.19 Signed-off-by: Sasha Levin --- diff --git a/queue-4.19/series b/queue-4.19/series index bb8224d04b6..69bb0ea98ec 100644 --- a/queue-4.19/series +++ b/queue-4.19/series @@ -86,3 +86,9 @@ mm-page_owner-handle-thp-splits-correctly.patch mm-zsmalloc.c-migration-can-leave-pages-in-zs_empty-indefinitely.patch mm-zsmalloc.c-fix-race-condition-in-zs_destroy_pool.patch xfs-fix-missing-ilock-unlock-when-xfs_setattr_nonsize-fails-due-to-edquot.patch +xfs-don-t-trip-over-uninitialized-buffer-on-extent-r.patch +xfs-move-fs-xfs-xfs_attr.h-to-fs-xfs-libxfs-xfs_attr.patch +xfs-add-helper-function-xfs_attr_try_sf_addname.patch +xfs-add-attibute-set-and-helper-functions.patch +xfs-add-attibute-remove-and-helper-functions.patch +xfs-always-rejoin-held-resources-during-defer-roll.patch diff --git a/queue-4.19/xfs-add-attibute-remove-and-helper-functions.patch b/queue-4.19/xfs-add-attibute-remove-and-helper-functions.patch new file mode 100644 index 00000000000..5687404df9f --- /dev/null +++ b/queue-4.19/xfs-add-attibute-remove-and-helper-functions.patch @@ -0,0 +1,90 @@ +From 02f0d2621d671f25f1c0f4f8023bd3630255f322 Mon Sep 17 00:00:00 2001 +From: Allison Henderson +Date: Wed, 24 Jul 2019 06:34:50 +0000 +Subject: xfs: Add attibute remove and helper functions + +commit 068f985a9e5ec70fde58d8f679994fdbbd093a36 upstream. + +This patch adds xfs_attr_remove_args. These sub-routines remove +the attributes specified in @args. We will use this later for setting +parent pointers as a deferred attribute operation. + +Signed-off-by: Allison Henderson +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_attr.c | 36 +++++++++++++++++++++++++----------- + fs/xfs/libxfs/xfs_attr.h | 1 + + 2 files changed, 26 insertions(+), 11 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c +index 25431ddba1fab..844ed87b19007 100644 +--- a/fs/xfs/libxfs/xfs_attr.c ++++ b/fs/xfs/libxfs/xfs_attr.c +@@ -289,6 +289,30 @@ xfs_attr_set_args( + return error; + } + ++/* ++ * Remove the attribute specified in @args. ++ */ ++int ++xfs_attr_remove_args( ++ struct xfs_da_args *args) ++{ ++ struct xfs_inode *dp = args->dp; ++ int error; ++ ++ if (!xfs_inode_hasattr(dp)) { ++ error = -ENOATTR; ++ } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) { ++ ASSERT(dp->i_afp->if_flags & XFS_IFINLINE); ++ error = xfs_attr_shortform_remove(args); ++ } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { ++ error = xfs_attr_leaf_removename(args); ++ } else { ++ error = xfs_attr_node_removename(args); ++ } ++ ++ return error; ++} ++ + int + xfs_attr_set( + struct xfs_inode *dp, +@@ -445,17 +469,7 @@ xfs_attr_remove( + */ + xfs_trans_ijoin(args.trans, dp, 0); + +- if (!xfs_inode_hasattr(dp)) { +- error = -ENOATTR; +- } else if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL) { +- ASSERT(dp->i_afp->if_flags & XFS_IFINLINE); +- error = xfs_attr_shortform_remove(&args); +- } else if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) { +- error = xfs_attr_leaf_removename(&args); +- } else { +- error = xfs_attr_node_removename(&args); +- } +- ++ error = xfs_attr_remove_args(&args); + if (error) + goto out; + +diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h +index f608ac8f306f9..bdf52a333f3f9 100644 +--- a/fs/xfs/libxfs/xfs_attr.h ++++ b/fs/xfs/libxfs/xfs_attr.h +@@ -142,6 +142,7 @@ int xfs_attr_set(struct xfs_inode *dp, const unsigned char *name, + unsigned char *value, int valuelen, int flags); + int xfs_attr_set_args(struct xfs_da_args *args, struct xfs_buf **leaf_bp); + int xfs_attr_remove(struct xfs_inode *dp, const unsigned char *name, int flags); ++int xfs_attr_remove_args(struct xfs_da_args *args); + int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize, + int flags, struct attrlist_cursor_kern *cursor); + +-- +2.20.1 + diff --git a/queue-4.19/xfs-add-attibute-set-and-helper-functions.patch b/queue-4.19/xfs-add-attibute-set-and-helper-functions.patch new file mode 100644 index 00000000000..650a0df3ec2 --- /dev/null +++ b/queue-4.19/xfs-add-attibute-set-and-helper-functions.patch @@ -0,0 +1,312 @@ +From 5c33001cfe3f8cc7f0e44ccfb096d9a599622cc2 Mon Sep 17 00:00:00 2001 +From: Allison Henderson +Date: Wed, 24 Jul 2019 06:34:49 +0000 +Subject: xfs: Add attibute set and helper functions + +commit 2f3cd8091963810d85e6a5dd6ed1247e10e9e6f2 upstream. + +This patch adds xfs_attr_set_args and xfs_bmap_set_attrforkoff. +These sub-routines set the attributes specified in @args. +We will use this later for setting parent pointers as a deferred +attribute operation. + +[dgc: remove attr fork init code from xfs_attr_set_args().] +[dgc: xfs_attr_try_sf_addname() NULLs args.trans after commit.] +[dgc: correct sf add error handling.] + +Signed-off-by: Allison Henderson +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_attr.c | 151 +++++++++++++++++++++------------------ + fs/xfs/libxfs/xfs_attr.h | 1 + + fs/xfs/libxfs/xfs_bmap.c | 49 ++++++++----- + fs/xfs/libxfs/xfs_bmap.h | 1 + + 4 files changed, 115 insertions(+), 87 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c +index c15a1debec907..25431ddba1fab 100644 +--- a/fs/xfs/libxfs/xfs_attr.c ++++ b/fs/xfs/libxfs/xfs_attr.c +@@ -215,9 +215,80 @@ xfs_attr_try_sf_addname( + xfs_trans_set_sync(args->trans); + + error2 = xfs_trans_commit(args->trans); ++ args->trans = NULL; + return error ? error : error2; + } + ++/* ++ * Set the attribute specified in @args. ++ */ ++int ++xfs_attr_set_args( ++ struct xfs_da_args *args, ++ struct xfs_buf **leaf_bp) ++{ ++ struct xfs_inode *dp = args->dp; ++ int error; ++ ++ /* ++ * If the attribute list is non-existent or a shortform list, ++ * upgrade it to a single-leaf-block attribute list. ++ */ ++ if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL || ++ (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS && ++ dp->i_d.di_anextents == 0)) { ++ ++ /* ++ * Build initial attribute list (if required). ++ */ ++ if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) ++ xfs_attr_shortform_create(args); ++ ++ /* ++ * Try to add the attr to the attribute list in the inode. ++ */ ++ error = xfs_attr_try_sf_addname(dp, args); ++ if (error != -ENOSPC) ++ return error; ++ ++ /* ++ * It won't fit in the shortform, transform to a leaf block. ++ * GROT: another possible req'mt for a double-split btree op. ++ */ ++ error = xfs_attr_shortform_to_leaf(args, leaf_bp); ++ if (error) ++ return error; ++ ++ /* ++ * Prevent the leaf buffer from being unlocked so that a ++ * concurrent AIL push cannot grab the half-baked leaf ++ * buffer and run into problems with the write verifier. ++ */ ++ xfs_trans_bhold(args->trans, *leaf_bp); ++ ++ error = xfs_defer_finish(&args->trans); ++ if (error) ++ return error; ++ ++ /* ++ * Commit the leaf transformation. We'll need another ++ * (linked) transaction to add the new attribute to the ++ * leaf. ++ */ ++ error = xfs_trans_roll_inode(&args->trans, dp); ++ if (error) ++ return error; ++ xfs_trans_bjoin(args->trans, *leaf_bp); ++ *leaf_bp = NULL; ++ } ++ ++ if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) ++ error = xfs_attr_leaf_addname(args); ++ else ++ error = xfs_attr_node_addname(args); ++ return error; ++} ++ + int + xfs_attr_set( + struct xfs_inode *dp, +@@ -282,73 +353,17 @@ xfs_attr_set( + error = xfs_trans_reserve_quota_nblks(args.trans, dp, args.total, 0, + rsvd ? XFS_QMOPT_RES_REGBLKS | XFS_QMOPT_FORCE_RES : + XFS_QMOPT_RES_REGBLKS); +- if (error) { +- xfs_iunlock(dp, XFS_ILOCK_EXCL); +- xfs_trans_cancel(args.trans); +- return error; +- } ++ if (error) ++ goto out_trans_cancel; + + xfs_trans_ijoin(args.trans, dp, 0); +- +- /* +- * If the attribute list is non-existent or a shortform list, +- * upgrade it to a single-leaf-block attribute list. +- */ +- if (dp->i_d.di_aformat == XFS_DINODE_FMT_LOCAL || +- (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS && +- dp->i_d.di_anextents == 0)) { +- +- /* +- * Build initial attribute list (if required). +- */ +- if (dp->i_d.di_aformat == XFS_DINODE_FMT_EXTENTS) +- xfs_attr_shortform_create(&args); +- +- /* +- * Try to add the attr to the attribute list in +- * the inode. +- */ +- error = xfs_attr_try_sf_addname(dp, &args); +- if (error != -ENOSPC) { +- xfs_iunlock(dp, XFS_ILOCK_EXCL); +- return error; +- } +- +- /* +- * It won't fit in the shortform, transform to a leaf block. +- * GROT: another possible req'mt for a double-split btree op. +- */ +- error = xfs_attr_shortform_to_leaf(&args, &leaf_bp); +- if (error) +- goto out; +- /* +- * Prevent the leaf buffer from being unlocked so that a +- * concurrent AIL push cannot grab the half-baked leaf +- * buffer and run into problems with the write verifier. +- */ +- xfs_trans_bhold(args.trans, leaf_bp); +- error = xfs_defer_finish(&args.trans); +- if (error) +- goto out; +- +- /* +- * Commit the leaf transformation. We'll need another (linked) +- * transaction to add the new attribute to the leaf, which +- * means that we have to hold & join the leaf buffer here too. +- */ +- error = xfs_trans_roll_inode(&args.trans, dp); +- if (error) +- goto out; +- xfs_trans_bjoin(args.trans, leaf_bp); +- leaf_bp = NULL; +- } +- +- if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) +- error = xfs_attr_leaf_addname(&args); +- else +- error = xfs_attr_node_addname(&args); ++ error = xfs_attr_set_args(&args, &leaf_bp); + if (error) +- goto out; ++ goto out_release_leaf; ++ if (!args.trans) { ++ /* shortform attribute has already been committed */ ++ goto out_unlock; ++ } + + /* + * If this is a synchronous mount, make sure that the +@@ -365,17 +380,17 @@ xfs_attr_set( + */ + xfs_trans_log_inode(args.trans, dp, XFS_ILOG_CORE); + error = xfs_trans_commit(args.trans); ++out_unlock: + xfs_iunlock(dp, XFS_ILOCK_EXCL); +- + return error; + +-out: ++out_release_leaf: + if (leaf_bp) + xfs_trans_brelse(args.trans, leaf_bp); ++out_trans_cancel: + if (args.trans) + xfs_trans_cancel(args.trans); +- xfs_iunlock(dp, XFS_ILOCK_EXCL); +- return error; ++ goto out_unlock; + } + + /* +diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h +index 033ff8c478e2e..f608ac8f306f9 100644 +--- a/fs/xfs/libxfs/xfs_attr.h ++++ b/fs/xfs/libxfs/xfs_attr.h +@@ -140,6 +140,7 @@ int xfs_attr_get(struct xfs_inode *ip, const unsigned char *name, + unsigned char *value, int *valuelenp, int flags); + int xfs_attr_set(struct xfs_inode *dp, const unsigned char *name, + unsigned char *value, int valuelen, int flags); ++int xfs_attr_set_args(struct xfs_da_args *args, struct xfs_buf **leaf_bp); + int xfs_attr_remove(struct xfs_inode *dp, const unsigned char *name, int flags); + int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize, + int flags, struct attrlist_cursor_kern *cursor); +diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c +index ab2465bc413af..06a7da8dbda5c 100644 +--- a/fs/xfs/libxfs/xfs_bmap.c ++++ b/fs/xfs/libxfs/xfs_bmap.c +@@ -1019,6 +1019,34 @@ xfs_bmap_add_attrfork_local( + return -EFSCORRUPTED; + } + ++/* Set an inode attr fork off based on the format */ ++int ++xfs_bmap_set_attrforkoff( ++ struct xfs_inode *ip, ++ int size, ++ int *version) ++{ ++ switch (ip->i_d.di_format) { ++ case XFS_DINODE_FMT_DEV: ++ ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; ++ break; ++ case XFS_DINODE_FMT_LOCAL: ++ case XFS_DINODE_FMT_EXTENTS: ++ case XFS_DINODE_FMT_BTREE: ++ ip->i_d.di_forkoff = xfs_attr_shortform_bytesfit(ip, size); ++ if (!ip->i_d.di_forkoff) ++ ip->i_d.di_forkoff = xfs_default_attroffset(ip) >> 3; ++ else if ((ip->i_mount->m_flags & XFS_MOUNT_ATTR2) && version) ++ *version = 2; ++ break; ++ default: ++ ASSERT(0); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + /* + * Convert inode from non-attributed to attributed. + * Must not be in a transaction, ip must not be locked. +@@ -1070,26 +1098,9 @@ xfs_bmap_add_attrfork( + + xfs_trans_ijoin(tp, ip, 0); + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); +- +- switch (ip->i_d.di_format) { +- case XFS_DINODE_FMT_DEV: +- ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; +- break; +- case XFS_DINODE_FMT_LOCAL: +- case XFS_DINODE_FMT_EXTENTS: +- case XFS_DINODE_FMT_BTREE: +- ip->i_d.di_forkoff = xfs_attr_shortform_bytesfit(ip, size); +- if (!ip->i_d.di_forkoff) +- ip->i_d.di_forkoff = xfs_default_attroffset(ip) >> 3; +- else if (mp->m_flags & XFS_MOUNT_ATTR2) +- version = 2; +- break; +- default: +- ASSERT(0); +- error = -EINVAL; ++ error = xfs_bmap_set_attrforkoff(ip, size, &version); ++ if (error) + goto trans_cancel; +- } +- + ASSERT(ip->i_afp == NULL); + ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP); + ip->i_afp->if_flags = XFS_IFEXTENTS; +diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h +index b6e9b639e731a..488dc8860fd7c 100644 +--- a/fs/xfs/libxfs/xfs_bmap.h ++++ b/fs/xfs/libxfs/xfs_bmap.h +@@ -183,6 +183,7 @@ void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno, + xfs_filblks_t len); + void xfs_trim_extent_eof(struct xfs_bmbt_irec *, struct xfs_inode *); + int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd); ++int xfs_bmap_set_attrforkoff(struct xfs_inode *ip, int size, int *version); + void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork); + void __xfs_bmap_add_free(struct xfs_trans *tp, xfs_fsblock_t bno, + xfs_filblks_t len, struct xfs_owner_info *oinfo, +-- +2.20.1 + diff --git a/queue-4.19/xfs-add-helper-function-xfs_attr_try_sf_addname.patch b/queue-4.19/xfs-add-helper-function-xfs_attr_try_sf_addname.patch new file mode 100644 index 00000000000..32c8525eba6 --- /dev/null +++ b/queue-4.19/xfs-add-helper-function-xfs_attr_try_sf_addname.patch @@ -0,0 +1,111 @@ +From 41282a0b552d5bb816b03a12e5885e5dd62158d8 Mon Sep 17 00:00:00 2001 +From: Allison Henderson +Date: Wed, 24 Jul 2019 06:34:48 +0000 +Subject: xfs: Add helper function xfs_attr_try_sf_addname + +commit 4c74a56b9de76bb6b581274b76b52535ad77c2a7 upstream. + +This patch adds a subroutine xfs_attr_try_sf_addname +used by xfs_attr_set. This subrotine will attempt to +add the attribute name specified in args in shortform, +as well and perform error handling previously done in +xfs_attr_set. + +This patch helps to pre-simplify xfs_attr_set for reviewing +purposes and reduce indentation. New function will be added +in the next patch. + +[dgc: moved commit to helper function, too.] + +Signed-off-by: Allison Henderson +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_attr.c | 53 +++++++++++++++++++++++----------------- + 1 file changed, 30 insertions(+), 23 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c +index c6299f82a6e49..c15a1debec907 100644 +--- a/fs/xfs/libxfs/xfs_attr.c ++++ b/fs/xfs/libxfs/xfs_attr.c +@@ -191,6 +191,33 @@ xfs_attr_calc_size( + return nblks; + } + ++STATIC int ++xfs_attr_try_sf_addname( ++ struct xfs_inode *dp, ++ struct xfs_da_args *args) ++{ ++ ++ struct xfs_mount *mp = dp->i_mount; ++ int error, error2; ++ ++ error = xfs_attr_shortform_addname(args); ++ if (error == -ENOSPC) ++ return error; ++ ++ /* ++ * Commit the shortform mods, and we're done. ++ * NOTE: this is also the error path (EEXIST, etc). ++ */ ++ if (!error && (args->flags & ATTR_KERNOTIME) == 0) ++ xfs_trans_ichgtime(args->trans, dp, XFS_ICHGTIME_CHG); ++ ++ if (mp->m_flags & XFS_MOUNT_WSYNC) ++ xfs_trans_set_sync(args->trans); ++ ++ error2 = xfs_trans_commit(args->trans); ++ return error ? error : error2; ++} ++ + int + xfs_attr_set( + struct xfs_inode *dp, +@@ -204,7 +231,7 @@ xfs_attr_set( + struct xfs_da_args args; + struct xfs_trans_res tres; + int rsvd = (flags & ATTR_ROOT) != 0; +- int error, err2, local; ++ int error, local; + + XFS_STATS_INC(mp, xs_attr_set); + +@@ -281,30 +308,10 @@ xfs_attr_set( + * Try to add the attr to the attribute list in + * the inode. + */ +- error = xfs_attr_shortform_addname(&args); ++ error = xfs_attr_try_sf_addname(dp, &args); + if (error != -ENOSPC) { +- /* +- * Commit the shortform mods, and we're done. +- * NOTE: this is also the error path (EEXIST, etc). +- */ +- ASSERT(args.trans != NULL); +- +- /* +- * If this is a synchronous mount, make sure that +- * the transaction goes to disk before returning +- * to the user. +- */ +- if (mp->m_flags & XFS_MOUNT_WSYNC) +- xfs_trans_set_sync(args.trans); +- +- if (!error && (flags & ATTR_KERNOTIME) == 0) { +- xfs_trans_ichgtime(args.trans, dp, +- XFS_ICHGTIME_CHG); +- } +- err2 = xfs_trans_commit(args.trans); + xfs_iunlock(dp, XFS_ILOCK_EXCL); +- +- return error ? error : err2; ++ return error; + } + + /* +-- +2.20.1 + diff --git a/queue-4.19/xfs-always-rejoin-held-resources-during-defer-roll.patch b/queue-4.19/xfs-always-rejoin-held-resources-during-defer-roll.patch new file mode 100644 index 00000000000..df1d92f1050 --- /dev/null +++ b/queue-4.19/xfs-always-rejoin-held-resources-during-defer-roll.patch @@ -0,0 +1,242 @@ +From 37fefdb505e151fd6e692a36d1b7da3c2d6ba3b0 Mon Sep 17 00:00:00 2001 +From: "Darrick J. Wong" +Date: Wed, 24 Jul 2019 06:34:51 +0000 +Subject: xfs: always rejoin held resources during defer roll + +commit 710d707d2fa9cf4c2aa9def129e71e99513466ea upstream. + +During testing of xfs/141 on a V4 filesystem, I observed some +inconsistent behavior with regards to resources that are held (i.e. +remain locked) across a defer roll. The transaction roll always gives +the defer roll function a new transaction, even if committing the old +transaction fails. However, the defer roll function only rejoins the +held resources if the transaction commit succeedied. This means that +callers of defer roll have to figure out whether the held resources are +attached to the transaction being passed back. + +Worse yet, if the defer roll was part of a defer finish call, we have a +third possibility: the defer finish could pass back a dirty transaction +with dirty held resources and an error code. + +The only sane way to handle all of these scenarios is to require that +the code that held the resource either cancel the transaction before +unlocking and releasing the resources, or use functions that detach +resources from a transaction properly (e.g. xfs_trans_brelse) if they +need to drop the reference before committing or cancelling the +transaction. + +In order to make this so, change the defer roll code to join held +resources to the new transaction unconditionally and fix all the bhold +callers to release the held buffers correctly. + +Signed-off-by: Darrick J. Wong +Reviewed-by: Brian Foster +[mcgrof: fixes kz#204223 ] +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_attr.c | 35 ++++++++++++----------------------- + fs/xfs/libxfs/xfs_attr.h | 2 +- + fs/xfs/libxfs/xfs_defer.c | 14 +++++++++----- + fs/xfs/xfs_dquot.c | 17 +++++++++-------- + 4 files changed, 31 insertions(+), 37 deletions(-) + +diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c +index 844ed87b19007..6410d3e00ce07 100644 +--- a/fs/xfs/libxfs/xfs_attr.c ++++ b/fs/xfs/libxfs/xfs_attr.c +@@ -224,10 +224,10 @@ xfs_attr_try_sf_addname( + */ + int + xfs_attr_set_args( +- struct xfs_da_args *args, +- struct xfs_buf **leaf_bp) ++ struct xfs_da_args *args) + { + struct xfs_inode *dp = args->dp; ++ struct xfs_buf *leaf_bp = NULL; + int error; + + /* +@@ -255,7 +255,7 @@ xfs_attr_set_args( + * It won't fit in the shortform, transform to a leaf block. + * GROT: another possible req'mt for a double-split btree op. + */ +- error = xfs_attr_shortform_to_leaf(args, leaf_bp); ++ error = xfs_attr_shortform_to_leaf(args, &leaf_bp); + if (error) + return error; + +@@ -263,23 +263,16 @@ xfs_attr_set_args( + * Prevent the leaf buffer from being unlocked so that a + * concurrent AIL push cannot grab the half-baked leaf + * buffer and run into problems with the write verifier. ++ * Once we're done rolling the transaction we can release ++ * the hold and add the attr to the leaf. + */ +- xfs_trans_bhold(args->trans, *leaf_bp); +- ++ xfs_trans_bhold(args->trans, leaf_bp); + error = xfs_defer_finish(&args->trans); +- if (error) +- return error; +- +- /* +- * Commit the leaf transformation. We'll need another +- * (linked) transaction to add the new attribute to the +- * leaf. +- */ +- error = xfs_trans_roll_inode(&args->trans, dp); +- if (error) ++ xfs_trans_bhold_release(args->trans, leaf_bp); ++ if (error) { ++ xfs_trans_brelse(args->trans, leaf_bp); + return error; +- xfs_trans_bjoin(args->trans, *leaf_bp); +- *leaf_bp = NULL; ++ } + } + + if (xfs_bmap_one_block(dp, XFS_ATTR_FORK)) +@@ -322,7 +315,6 @@ xfs_attr_set( + int flags) + { + struct xfs_mount *mp = dp->i_mount; +- struct xfs_buf *leaf_bp = NULL; + struct xfs_da_args args; + struct xfs_trans_res tres; + int rsvd = (flags & ATTR_ROOT) != 0; +@@ -381,9 +373,9 @@ xfs_attr_set( + goto out_trans_cancel; + + xfs_trans_ijoin(args.trans, dp, 0); +- error = xfs_attr_set_args(&args, &leaf_bp); ++ error = xfs_attr_set_args(&args); + if (error) +- goto out_release_leaf; ++ goto out_trans_cancel; + if (!args.trans) { + /* shortform attribute has already been committed */ + goto out_unlock; +@@ -408,9 +400,6 @@ xfs_attr_set( + xfs_iunlock(dp, XFS_ILOCK_EXCL); + return error; + +-out_release_leaf: +- if (leaf_bp) +- xfs_trans_brelse(args.trans, leaf_bp); + out_trans_cancel: + if (args.trans) + xfs_trans_cancel(args.trans); +diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h +index bdf52a333f3f9..cc04ee0aacfbe 100644 +--- a/fs/xfs/libxfs/xfs_attr.h ++++ b/fs/xfs/libxfs/xfs_attr.h +@@ -140,7 +140,7 @@ int xfs_attr_get(struct xfs_inode *ip, const unsigned char *name, + unsigned char *value, int *valuelenp, int flags); + int xfs_attr_set(struct xfs_inode *dp, const unsigned char *name, + unsigned char *value, int valuelen, int flags); +-int xfs_attr_set_args(struct xfs_da_args *args, struct xfs_buf **leaf_bp); ++int xfs_attr_set_args(struct xfs_da_args *args); + int xfs_attr_remove(struct xfs_inode *dp, const unsigned char *name, int flags); + int xfs_attr_remove_args(struct xfs_da_args *args); + int xfs_attr_list(struct xfs_inode *dp, char *buffer, int bufsize, +diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c +index e792b167150a0..c52beee31836a 100644 +--- a/fs/xfs/libxfs/xfs_defer.c ++++ b/fs/xfs/libxfs/xfs_defer.c +@@ -266,13 +266,15 @@ xfs_defer_trans_roll( + + trace_xfs_defer_trans_roll(tp, _RET_IP_); + +- /* Roll the transaction. */ ++ /* ++ * Roll the transaction. Rolling always given a new transaction (even ++ * if committing the old one fails!) to hand back to the caller, so we ++ * join the held resources to the new transaction so that we always ++ * return with the held resources joined to @tpp, no matter what ++ * happened. ++ */ + error = xfs_trans_roll(tpp); + tp = *tpp; +- if (error) { +- trace_xfs_defer_trans_roll_error(tp, error); +- return error; +- } + + /* Rejoin the joined inodes. */ + for (i = 0; i < ipcount; i++) +@@ -284,6 +286,8 @@ xfs_defer_trans_roll( + xfs_trans_bhold(tp, bplist[i]); + } + ++ if (error) ++ trace_xfs_defer_trans_roll_error(tp, error); + return error; + } + +diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c +index 87e6dd5326d5d..a1af984e4913e 100644 +--- a/fs/xfs/xfs_dquot.c ++++ b/fs/xfs/xfs_dquot.c +@@ -277,7 +277,8 @@ xfs_dquot_set_prealloc_limits(struct xfs_dquot *dqp) + + /* + * Ensure that the given in-core dquot has a buffer on disk backing it, and +- * return the buffer. This is called when the bmapi finds a hole. ++ * return the buffer locked and held. This is called when the bmapi finds a ++ * hole. + */ + STATIC int + xfs_dquot_disk_alloc( +@@ -355,13 +356,14 @@ xfs_dquot_disk_alloc( + * If everything succeeds, the caller of this function is returned a + * buffer that is locked and held to the transaction. The caller + * is responsible for unlocking any buffer passed back, either +- * manually or by committing the transaction. ++ * manually or by committing the transaction. On error, the buffer is ++ * released and not passed back. + */ + xfs_trans_bhold(tp, bp); + error = xfs_defer_finish(tpp); +- tp = *tpp; + if (error) { +- xfs_buf_relse(bp); ++ xfs_trans_bhold_release(*tpp, bp); ++ xfs_trans_brelse(*tpp, bp); + return error; + } + *bpp = bp; +@@ -521,7 +523,6 @@ xfs_qm_dqread_alloc( + struct xfs_buf **bpp) + { + struct xfs_trans *tp; +- struct xfs_buf *bp; + int error; + + error = xfs_trans_alloc(mp, &M_RES(mp)->tr_qm_dqalloc, +@@ -529,7 +530,7 @@ xfs_qm_dqread_alloc( + if (error) + goto err; + +- error = xfs_dquot_disk_alloc(&tp, dqp, &bp); ++ error = xfs_dquot_disk_alloc(&tp, dqp, bpp); + if (error) + goto err_cancel; + +@@ -539,10 +540,10 @@ xfs_qm_dqread_alloc( + * Buffer was held to the transaction, so we have to unlock it + * manually here because we're not passing it back. + */ +- xfs_buf_relse(bp); ++ xfs_buf_relse(*bpp); ++ *bpp = NULL; + goto err; + } +- *bpp = bp; + return 0; + + err_cancel: +-- +2.20.1 + diff --git a/queue-4.19/xfs-don-t-trip-over-uninitialized-buffer-on-extent-r.patch b/queue-4.19/xfs-don-t-trip-over-uninitialized-buffer-on-extent-r.patch new file mode 100644 index 00000000000..ae46c7f2485 --- /dev/null +++ b/queue-4.19/xfs-don-t-trip-over-uninitialized-buffer-on-extent-r.patch @@ -0,0 +1,77 @@ +From 09ef83bab28fcae868a16305a29250452a7e0e86 Mon Sep 17 00:00:00 2001 +From: Brian Foster +Date: Wed, 24 Jul 2019 06:34:46 +0000 +Subject: xfs: don't trip over uninitialized buffer on extent read of corrupted + inode + +commit 6958d11f77d45db80f7e22a21a74d4d5f44dc667 upstream. + +We've had rather rare reports of bmap btree block corruption where +the bmap root block has a level count of zero. The root cause of the +corruption is so far unknown. We do have verifier checks to detect +this form of on-disk corruption, but this doesn't cover a memory +corruption variant of the problem. The latter is a reasonable +possibility because the root block is part of the inode fork and can +reside in-core for some time before inode extents are read. + +If this occurs, it leads to a system crash such as the following: + + BUG: unable to handle kernel paging request at ffffffff00000221 + PF error: [normal kernel read fault] + ... + RIP: 0010:xfs_trans_brelse+0xf/0x200 [xfs] + ... + Call Trace: + xfs_iread_extents+0x379/0x540 [xfs] + xfs_file_iomap_begin_delay+0x11a/0xb40 [xfs] + ? xfs_attr_get+0xd1/0x120 [xfs] + ? iomap_write_begin.constprop.40+0x2d0/0x2d0 + xfs_file_iomap_begin+0x4c4/0x6d0 [xfs] + ? __vfs_getxattr+0x53/0x70 + ? iomap_write_begin.constprop.40+0x2d0/0x2d0 + iomap_apply+0x63/0x130 + ? iomap_write_begin.constprop.40+0x2d0/0x2d0 + iomap_file_buffered_write+0x62/0x90 + ? iomap_write_begin.constprop.40+0x2d0/0x2d0 + xfs_file_buffered_aio_write+0xe4/0x3b0 [xfs] + __vfs_write+0x150/0x1b0 + vfs_write+0xba/0x1c0 + ksys_pwrite64+0x64/0xa0 + do_syscall_64+0x5a/0x1d0 + entry_SYSCALL_64_after_hwframe+0x49/0xbe + +The crash occurs because xfs_iread_extents() attempts to release an +uninitialized buffer pointer as the level == 0 value prevented the +buffer from ever being allocated or read. Change the level > 0 +assert to an explicit error check in xfs_iread_extents() to avoid +crashing the kernel in the event of localized, in-core inode +corruption. + +Signed-off-by: Brian Foster +Reviewed-by: Darrick J. Wong +Signed-off-by: Darrick J. Wong +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/libxfs/xfs_bmap.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c +index 3a496ffe6551c..ab2465bc413af 100644 +--- a/fs/xfs/libxfs/xfs_bmap.c ++++ b/fs/xfs/libxfs/xfs_bmap.c +@@ -1178,7 +1178,10 @@ xfs_iread_extents( + * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. + */ + level = be16_to_cpu(block->bb_level); +- ASSERT(level > 0); ++ if (unlikely(level == 0)) { ++ XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); ++ return -EFSCORRUPTED; ++ } + pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes); + bno = be64_to_cpu(*pp); + +-- +2.20.1 + diff --git a/queue-4.19/xfs-move-fs-xfs-xfs_attr.h-to-fs-xfs-libxfs-xfs_attr.patch b/queue-4.19/xfs-move-fs-xfs-xfs_attr.h-to-fs-xfs-libxfs-xfs_attr.patch new file mode 100644 index 00000000000..3d141bfdd8d --- /dev/null +++ b/queue-4.19/xfs-move-fs-xfs-xfs_attr.h-to-fs-xfs-libxfs-xfs_attr.patch @@ -0,0 +1,28 @@ +From 3c45920844819ed92b1b115ae2f8ce0a0259136b Mon Sep 17 00:00:00 2001 +From: Allison Henderson +Date: Wed, 24 Jul 2019 06:34:47 +0000 +Subject: xfs: Move fs/xfs/xfs_attr.h to fs/xfs/libxfs/xfs_attr.h + +commit e2421f0b5ff3ce279573036f5cfcb0ce28b422a9 upstream. + +This patch moves fs/xfs/xfs_attr.h to fs/xfs/libxfs/xfs_attr.h +since xfs_attr.c is in libxfs. We will need these later in +xfsprogs. + +Signed-off-by: Allison Henderson +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Signed-off-by: Luis Chamberlain +Signed-off-by: Sasha Levin +--- + fs/xfs/{ => libxfs}/xfs_attr.h | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + rename fs/xfs/{ => libxfs}/xfs_attr.h (100%) + +diff --git a/fs/xfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h +similarity index 100% +rename from fs/xfs/xfs_attr.h +rename to fs/xfs/libxfs/xfs_attr.h +-- +2.20.1 +