2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2005-11-02 11:58:39 +08:00
|
|
|
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
|
|
|
|
* All Rights Reserved.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2005-11-02 11:58:39 +08:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
2005-04-17 06:20:36 +08:00
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
2005-11-02 11:58:39 +08:00
|
|
|
* This program is distributed in the hope that it would be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2005-11-02 11:58:39 +08:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write the Free Software Foundation,
|
|
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
#include "xfs.h"
|
2005-11-02 11:38:42 +08:00
|
|
|
#include "xfs_fs.h"
|
2013-10-23 07:51:50 +08:00
|
|
|
#include "xfs_format.h"
|
2013-10-23 07:50:10 +08:00
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "xfs_mount.h"
|
|
|
|
#include "xfs_inode.h"
|
2013-10-23 07:50:10 +08:00
|
|
|
#include "xfs_trans.h"
|
2005-11-02 11:38:42 +08:00
|
|
|
#include "xfs_inode_item.h"
|
2008-04-10 10:22:24 +08:00
|
|
|
#include "xfs_error.h"
|
2009-12-15 07:14:59 +08:00
|
|
|
#include "xfs_trace.h"
|
2013-10-23 07:50:10 +08:00
|
|
|
#include "xfs_trans_priv.h"
|
2013-12-13 08:00:43 +08:00
|
|
|
#include "xfs_log.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
kmem_zone_t *xfs_ili_zone; /* inode log item zone */
|
|
|
|
|
2010-06-23 16:11:15 +08:00
|
|
|
static inline struct xfs_inode_log_item *INODE_ITEM(struct xfs_log_item *lip)
|
|
|
|
{
|
|
|
|
return container_of(lip, struct xfs_inode_log_item, ili_item);
|
|
|
|
}
|
|
|
|
|
2013-08-12 18:50:04 +08:00
|
|
|
STATIC void
|
2013-12-13 08:00:43 +08:00
|
|
|
xfs_inode_item_data_fork_size(
|
|
|
|
struct xfs_inode_log_item *iip,
|
2013-08-12 18:50:04 +08:00
|
|
|
int *nvecs,
|
|
|
|
int *nbytes)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
2013-08-12 18:50:04 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (ip->i_d.di_format) {
|
|
|
|
case XFS_DINODE_FMT_EXTENTS:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DEXT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_d.di_nextents > 0 &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_df.if_bytes > 0) {
|
|
|
|
/* worst case, doesn't subtract delalloc extents */
|
|
|
|
*nbytes += XFS_IFORK_DSIZE(ip);
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_BTREE:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DBROOT) &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_df.if_broot_bytes > 0) {
|
|
|
|
*nbytes += ip->i_df.if_broot_bytes;
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_LOCAL:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DDATA) &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_df.if_bytes > 0) {
|
|
|
|
*nbytes += roundup(ip->i_df.if_bytes, 4);
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case XFS_DINODE_FMT_DEV:
|
|
|
|
case XFS_DINODE_FMT_UUID:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
2013-12-13 08:00:43 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-12-13 08:00:43 +08:00
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_attr_fork_size(
|
|
|
|
struct xfs_inode_log_item *iip,
|
|
|
|
int *nvecs,
|
|
|
|
int *nbytes)
|
|
|
|
{
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (ip->i_d.di_aformat) {
|
|
|
|
case XFS_DINODE_FMT_EXTENTS:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_AEXT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_d.di_anextents > 0 &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_afp->if_bytes > 0) {
|
|
|
|
/* worst case, doesn't subtract unused space */
|
|
|
|
*nbytes += XFS_IFORK_ASIZE(ip);
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_BTREE:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_ABROOT) &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_afp->if_broot_bytes > 0) {
|
|
|
|
*nbytes += ip->i_afp->if_broot_bytes;
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_LOCAL:
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_ADATA) &&
|
2013-08-12 18:50:04 +08:00
|
|
|
ip->i_afp->if_bytes > 0) {
|
|
|
|
*nbytes += roundup(ip->i_afp->if_bytes, 4);
|
|
|
|
*nvecs += 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:00:43 +08:00
|
|
|
/*
|
|
|
|
* This returns the number of iovecs needed to log the given inode item.
|
|
|
|
*
|
|
|
|
* We need one iovec for the inode log format structure, one for the
|
|
|
|
* inode core, and possibly one for the inode data/extents/b-tree root
|
|
|
|
* and one for the inode attribute data/extents/b-tree root.
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_size(
|
|
|
|
struct xfs_log_item *lip,
|
|
|
|
int *nvecs,
|
|
|
|
int *nbytes)
|
|
|
|
{
|
|
|
|
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
|
|
|
|
|
|
|
*nvecs += 2;
|
|
|
|
*nbytes += sizeof(struct xfs_inode_log_format) +
|
2016-02-09 13:54:58 +08:00
|
|
|
xfs_log_dinode_size(ip->i_d.di_version);
|
2013-12-13 08:00:43 +08:00
|
|
|
|
|
|
|
xfs_inode_item_data_fork_size(iip, nvecs, nbytes);
|
|
|
|
if (XFS_IFORK_Q(ip))
|
|
|
|
xfs_inode_item_attr_fork_size(iip, nvecs, nbytes);
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:00:43 +08:00
|
|
|
STATIC void
|
2013-12-13 08:00:43 +08:00
|
|
|
xfs_inode_item_format_data_fork(
|
|
|
|
struct xfs_inode_log_item *iip,
|
2013-12-13 08:34:02 +08:00
|
|
|
struct xfs_inode_log_format *ilf,
|
|
|
|
struct xfs_log_vec *lv,
|
|
|
|
struct xfs_log_iovec **vecp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
2005-04-17 06:20:36 +08:00
|
|
|
size_t data_bytes;
|
|
|
|
|
|
|
|
switch (ip->i_d.di_format) {
|
|
|
|
case XFS_DINODE_FMT_EXTENTS:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
|
|
|
|
XFS_ILOG_DEV | XFS_ILOG_UUID);
|
|
|
|
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DEXT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_d.di_nextents > 0 &&
|
|
|
|
ip->i_df.if_bytes > 0) {
|
2013-12-13 08:34:04 +08:00
|
|
|
struct xfs_bmbt_rec *p;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
ASSERT(ip->i_df.if_u1.if_extents != NULL);
|
2016-11-08 09:59:42 +08:00
|
|
|
ASSERT(xfs_iext_count(&ip->i_df) > 0);
|
2013-12-13 08:34:04 +08:00
|
|
|
|
|
|
|
p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IEXT);
|
|
|
|
data_bytes = xfs_iextents_copy(ip, p, XFS_DATA_FORK);
|
|
|
|
xlog_finish_iovec(lv, *vecp, data_bytes);
|
|
|
|
|
|
|
|
ASSERT(data_bytes <= ip->i_df.if_bytes);
|
|
|
|
|
|
|
|
ilf->ilf_dsize = data_bytes;
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_DEXT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_BTREE:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_DDATA | XFS_ILOG_DEXT |
|
|
|
|
XFS_ILOG_DEV | XFS_ILOG_UUID);
|
|
|
|
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DBROOT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_df.if_broot_bytes > 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ASSERT(ip->i_df.if_broot != NULL);
|
2013-12-13 08:34:02 +08:00
|
|
|
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IBROOT,
|
2013-12-13 08:00:43 +08:00
|
|
|
ip->i_df.if_broot,
|
|
|
|
ip->i_df.if_broot_bytes);
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_dsize = ip->i_df.if_broot_bytes;
|
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
ASSERT(!(iip->ili_fields &
|
2012-02-29 17:53:53 +08:00
|
|
|
XFS_ILOG_DBROOT));
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_DBROOT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_LOCAL:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_DEXT | XFS_ILOG_DBROOT |
|
|
|
|
XFS_ILOG_DEV | XFS_ILOG_UUID);
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_DDATA) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_df.if_bytes > 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Round i_bytes up to a word boundary.
|
|
|
|
* The underlying memory is guaranteed to
|
|
|
|
* to be there by xfs_idata_realloc().
|
|
|
|
*/
|
|
|
|
data_bytes = roundup(ip->i_df.if_bytes, 4);
|
2013-12-13 08:00:43 +08:00
|
|
|
ASSERT(ip->i_df.if_real_bytes == 0 ||
|
2016-04-06 05:53:29 +08:00
|
|
|
ip->i_df.if_real_bytes >= data_bytes);
|
2013-12-13 08:00:43 +08:00
|
|
|
ASSERT(ip->i_df.if_u1.if_data != NULL);
|
|
|
|
ASSERT(ip->i_d.di_size > 0);
|
2013-12-13 08:34:02 +08:00
|
|
|
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_ILOCAL,
|
2013-12-13 08:00:43 +08:00
|
|
|
ip->i_df.if_u1.if_data, data_bytes);
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_dsize = (unsigned)data_bytes;
|
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_DDATA;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_DEV:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
|
|
|
|
XFS_ILOG_DEXT | XFS_ILOG_UUID);
|
2013-12-13 08:34:02 +08:00
|
|
|
if (iip->ili_fields & XFS_ILOG_DEV)
|
|
|
|
ilf->ilf_u.ilfu_rdev = ip->i_df.if_u2.if_rdev;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_UUID:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_DDATA | XFS_ILOG_DBROOT |
|
|
|
|
XFS_ILOG_DEXT | XFS_ILOG_DEV);
|
2013-12-13 08:34:02 +08:00
|
|
|
if (iip->ili_fields & XFS_ILOG_UUID)
|
|
|
|
ilf->ilf_u.ilfu_uuid = ip->i_df.if_u2.if_uuid;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
2013-12-13 08:00:43 +08:00
|
|
|
}
|
|
|
|
|
2013-12-13 08:00:43 +08:00
|
|
|
STATIC void
|
2013-12-13 08:00:43 +08:00
|
|
|
xfs_inode_item_format_attr_fork(
|
|
|
|
struct xfs_inode_log_item *iip,
|
2013-12-13 08:34:02 +08:00
|
|
|
struct xfs_inode_log_format *ilf,
|
|
|
|
struct xfs_log_vec *lv,
|
|
|
|
struct xfs_log_iovec **vecp)
|
2013-12-13 08:00:43 +08:00
|
|
|
{
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
|
|
|
size_t data_bytes;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (ip->i_d.di_aformat) {
|
|
|
|
case XFS_DINODE_FMT_EXTENTS:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT);
|
|
|
|
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_AEXT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_d.di_anextents > 0 &&
|
|
|
|
ip->i_afp->if_bytes > 0) {
|
2013-12-13 08:34:04 +08:00
|
|
|
struct xfs_bmbt_rec *p;
|
|
|
|
|
2016-11-08 09:59:42 +08:00
|
|
|
ASSERT(xfs_iext_count(ip->i_afp) ==
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_d.di_anextents);
|
2010-07-20 15:54:45 +08:00
|
|
|
ASSERT(ip->i_afp->if_u1.if_extents != NULL);
|
2013-12-13 08:34:04 +08:00
|
|
|
|
|
|
|
p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT);
|
|
|
|
data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK);
|
|
|
|
xlog_finish_iovec(lv, *vecp, data_bytes);
|
|
|
|
|
|
|
|
ilf->ilf_asize = data_bytes;
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_AEXT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_BTREE:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_ADATA | XFS_ILOG_AEXT);
|
|
|
|
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_ABROOT) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_afp->if_broot_bytes > 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ASSERT(ip->i_afp->if_broot != NULL);
|
2012-02-29 17:53:53 +08:00
|
|
|
|
2013-12-13 08:34:02 +08:00
|
|
|
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_BROOT,
|
2013-12-13 08:00:43 +08:00
|
|
|
ip->i_afp->if_broot,
|
|
|
|
ip->i_afp->if_broot_bytes);
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_asize = ip->i_afp->if_broot_bytes;
|
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_ABROOT;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XFS_DINODE_FMT_LOCAL:
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &=
|
2012-02-29 17:53:53 +08:00
|
|
|
~(XFS_ILOG_AEXT | XFS_ILOG_ABROOT);
|
|
|
|
|
2012-02-29 17:53:54 +08:00
|
|
|
if ((iip->ili_fields & XFS_ILOG_ADATA) &&
|
2012-02-29 17:53:53 +08:00
|
|
|
ip->i_afp->if_bytes > 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Round i_bytes up to a word boundary.
|
|
|
|
* The underlying memory is guaranteed to
|
|
|
|
* to be there by xfs_idata_realloc().
|
|
|
|
*/
|
|
|
|
data_bytes = roundup(ip->i_afp->if_bytes, 4);
|
2013-12-13 08:00:43 +08:00
|
|
|
ASSERT(ip->i_afp->if_real_bytes == 0 ||
|
2016-04-06 05:53:29 +08:00
|
|
|
ip->i_afp->if_real_bytes >= data_bytes);
|
2013-12-13 08:00:43 +08:00
|
|
|
ASSERT(ip->i_afp->if_u1.if_data != NULL);
|
2013-12-13 08:34:02 +08:00
|
|
|
xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL,
|
2013-12-13 08:00:43 +08:00
|
|
|
ip->i_afp->if_u1.if_data,
|
|
|
|
data_bytes);
|
2013-12-13 08:34:02 +08:00
|
|
|
ilf->ilf_asize = (unsigned)data_bytes;
|
|
|
|
ilf->ilf_size++;
|
2012-02-29 17:53:53 +08:00
|
|
|
} else {
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields &= ~XFS_ILOG_ADATA;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
break;
|
|
|
|
}
|
2013-12-13 08:00:43 +08:00
|
|
|
}
|
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
static void
|
2016-02-09 13:54:58 +08:00
|
|
|
xfs_inode_to_log_dinode(
|
|
|
|
struct xfs_inode *ip,
|
2016-02-09 13:54:58 +08:00
|
|
|
struct xfs_log_dinode *to,
|
|
|
|
xfs_lsn_t lsn)
|
2016-02-09 13:54:58 +08:00
|
|
|
{
|
2016-02-09 13:54:58 +08:00
|
|
|
struct xfs_icdinode *from = &ip->i_d;
|
|
|
|
struct inode *inode = VFS_I(ip);
|
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_magic = XFS_DINODE_MAGIC;
|
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_version = from->di_version;
|
|
|
|
to->di_format = from->di_format;
|
|
|
|
to->di_uid = from->di_uid;
|
|
|
|
to->di_gid = from->di_gid;
|
|
|
|
to->di_projid_lo = from->di_projid_lo;
|
|
|
|
to->di_projid_hi = from->di_projid_hi;
|
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
memset(to->di_pad, 0, sizeof(to->di_pad));
|
2016-02-09 13:54:58 +08:00
|
|
|
memset(to->di_pad3, 0, sizeof(to->di_pad3));
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_atime.t_sec = inode->i_atime.tv_sec;
|
|
|
|
to->di_atime.t_nsec = inode->i_atime.tv_nsec;
|
|
|
|
to->di_mtime.t_sec = inode->i_mtime.tv_sec;
|
|
|
|
to->di_mtime.t_nsec = inode->i_mtime.tv_nsec;
|
|
|
|
to->di_ctime.t_sec = inode->i_ctime.tv_sec;
|
|
|
|
to->di_ctime.t_nsec = inode->i_ctime.tv_nsec;
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_nlink = inode->i_nlink;
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_gen = inode->i_generation;
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_mode = inode->i_mode;
|
2016-02-09 13:54:58 +08:00
|
|
|
|
|
|
|
to->di_size = from->di_size;
|
|
|
|
to->di_nblocks = from->di_nblocks;
|
|
|
|
to->di_extsize = from->di_extsize;
|
|
|
|
to->di_nextents = from->di_nextents;
|
|
|
|
to->di_anextents = from->di_anextents;
|
|
|
|
to->di_forkoff = from->di_forkoff;
|
|
|
|
to->di_aformat = from->di_aformat;
|
|
|
|
to->di_dmevmask = from->di_dmevmask;
|
|
|
|
to->di_dmstate = from->di_dmstate;
|
|
|
|
to->di_flags = from->di_flags;
|
|
|
|
|
|
|
|
if (from->di_version == 3) {
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_changecount = inode->i_version;
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_crtime.t_sec = from->di_crtime.t_sec;
|
|
|
|
to->di_crtime.t_nsec = from->di_crtime.t_nsec;
|
|
|
|
to->di_flags2 = from->di_flags2;
|
2016-10-04 00:11:43 +08:00
|
|
|
to->di_cowextsize = from->di_cowextsize;
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_ino = ip->i_ino;
|
|
|
|
to->di_lsn = lsn;
|
|
|
|
memset(to->di_pad2, 0, sizeof(to->di_pad2));
|
|
|
|
uuid_copy(&to->di_uuid, &ip->i_mount->m_sb.sb_meta_uuid);
|
2016-02-09 13:54:58 +08:00
|
|
|
to->di_flushiter = 0;
|
|
|
|
} else {
|
|
|
|
to->di_flushiter = from->di_flushiter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Format the inode core. Current timestamp data is only in the VFS inode
|
|
|
|
* fields, so we need to grab them from there. Hence rather than just copying
|
|
|
|
* the XFS inode core structure, format the fields directly into the iovec.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_inode_item_format_core(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct xfs_log_vec *lv,
|
|
|
|
struct xfs_log_iovec **vecp)
|
|
|
|
{
|
|
|
|
struct xfs_log_dinode *dic;
|
|
|
|
|
|
|
|
dic = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_ICORE);
|
2016-02-09 13:54:58 +08:00
|
|
|
xfs_inode_to_log_dinode(ip, dic, ip->i_itemp->ili_item.li_lsn);
|
2016-02-09 13:54:58 +08:00
|
|
|
xlog_finish_iovec(lv, *vecp, xfs_log_dinode_size(ip->i_d.di_version));
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:00:43 +08:00
|
|
|
/*
|
|
|
|
* This is called to fill in the vector of log iovecs for the given inode
|
|
|
|
* log item. It fills the first item with an inode log format structure,
|
|
|
|
* the second with the on-disk inode structure, and a possible third and/or
|
|
|
|
* fourth with the inode data/extents/b-tree root and inode attributes
|
|
|
|
* data/extents/b-tree root.
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_format(
|
|
|
|
struct xfs_log_item *lip,
|
2013-12-13 08:34:02 +08:00
|
|
|
struct xfs_log_vec *lv)
|
2013-12-13 08:00:43 +08:00
|
|
|
{
|
|
|
|
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
2013-12-13 08:34:02 +08:00
|
|
|
struct xfs_inode_log_format *ilf;
|
|
|
|
struct xfs_log_iovec *vecp = NULL;
|
2013-12-13 08:00:43 +08:00
|
|
|
|
2014-05-20 05:46:40 +08:00
|
|
|
ASSERT(ip->i_d.di_version > 1);
|
|
|
|
|
2013-12-13 08:34:05 +08:00
|
|
|
ilf = xlog_prepare_iovec(lv, &vecp, XLOG_REG_TYPE_IFORMAT);
|
|
|
|
ilf->ilf_type = XFS_LI_INODE;
|
|
|
|
ilf->ilf_ino = ip->i_ino;
|
|
|
|
ilf->ilf_blkno = ip->i_imap.im_blkno;
|
|
|
|
ilf->ilf_len = ip->i_imap.im_len;
|
|
|
|
ilf->ilf_boffset = ip->i_imap.im_boffset;
|
|
|
|
ilf->ilf_fields = XFS_ILOG_CORE;
|
|
|
|
ilf->ilf_size = 2; /* format + core */
|
|
|
|
xlog_finish_iovec(lv, vecp, sizeof(struct xfs_inode_log_format));
|
2013-12-13 08:00:43 +08:00
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
xfs_inode_item_format_core(ip, lv, &vecp);
|
2013-12-13 08:34:02 +08:00
|
|
|
xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp);
|
2013-12-13 08:00:43 +08:00
|
|
|
if (XFS_IFORK_Q(ip)) {
|
2013-12-13 08:34:02 +08:00
|
|
|
xfs_inode_item_format_attr_fork(iip, ilf, lv, &vecp);
|
2013-12-13 08:00:43 +08:00
|
|
|
} else {
|
|
|
|
iip->ili_fields &=
|
|
|
|
~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT | XFS_ILOG_AEXT);
|
|
|
|
}
|
|
|
|
|
2013-12-13 08:34:05 +08:00
|
|
|
/* update the format with the exact fields we actually logged */
|
|
|
|
ilf->ilf_fields |= (iip->ili_fields & ~XFS_ILOG_TIMESTAMP);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called to pin the inode associated with the inode log
|
2010-02-18 20:43:22 +08:00
|
|
|
* item in memory so it cannot be written out.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_pin(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_log_item *lip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode;
|
2010-02-18 20:43:22 +08:00
|
|
|
|
2010-06-23 16:11:15 +08:00
|
|
|
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
|
|
|
|
|
|
|
trace_xfs_inode_pin(ip, _RET_IP_);
|
|
|
|
atomic_inc(&ip->i_pincount);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called to unpin the inode associated with the inode log
|
|
|
|
* item which was previously pinned with a call to xfs_inode_item_pin().
|
2010-02-18 20:43:22 +08:00
|
|
|
*
|
|
|
|
* Also wake up anyone in xfs_iunpin_wait() if the count goes to 0.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_unpin(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_log_item *lip,
|
2010-06-23 16:11:15 +08:00
|
|
|
int remove)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode;
|
2010-02-18 20:43:22 +08:00
|
|
|
|
2010-03-08 08:24:07 +08:00
|
|
|
trace_xfs_inode_unpin(ip, _RET_IP_);
|
2010-02-18 20:43:22 +08:00
|
|
|
ASSERT(atomic_read(&ip->i_pincount) > 0);
|
|
|
|
if (atomic_dec_and_test(&ip->i_pincount))
|
2011-12-19 04:00:10 +08:00
|
|
|
wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC uint
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
xfs_inode_item_push(
|
|
|
|
struct xfs_log_item *lip,
|
|
|
|
struct list_head *buffer_list)
|
2016-04-06 07:47:21 +08:00
|
|
|
__releases(&lip->li_ailp->xa_lock)
|
|
|
|
__acquires(&lip->li_ailp->xa_lock)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
struct xfs_buf *bp = NULL;
|
|
|
|
uint rval = XFS_ITEM_SUCCESS;
|
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-23 16:11:15 +08:00
|
|
|
if (xfs_ipincount(ip) > 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return XFS_ITEM_PINNED;
|
|
|
|
|
2010-06-23 16:11:15 +08:00
|
|
|
if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED))
|
2005-04-17 06:20:36 +08:00
|
|
|
return XFS_ITEM_LOCKED;
|
|
|
|
|
2012-04-23 13:58:36 +08:00
|
|
|
/*
|
|
|
|
* Re-check the pincount now that we stabilized the value by
|
|
|
|
* taking the ilock.
|
|
|
|
*/
|
|
|
|
if (xfs_ipincount(ip) > 0) {
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
rval = XFS_ITEM_PINNED;
|
|
|
|
goto out_unlock;
|
2012-04-23 13:58:36 +08:00
|
|
|
}
|
|
|
|
|
2012-06-11 22:39:43 +08:00
|
|
|
/*
|
|
|
|
* Stale inode items should force out the iclog.
|
|
|
|
*/
|
|
|
|
if (ip->i_flags & XFS_ISTALE) {
|
|
|
|
rval = XFS_ITEM_PINNED;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
/*
|
|
|
|
* Someone else is already flushing the inode. Nothing we can do
|
|
|
|
* here but wait for the flush to finish and remove the item from
|
|
|
|
* the AIL.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!xfs_iflock_nowait(ip)) {
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
rval = XFS_ITEM_FLUSHING;
|
|
|
|
goto out_unlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
ASSERT(iip->ili_fields != 0 || XFS_FORCED_SHUTDOWN(ip->i_mount));
|
|
|
|
ASSERT(iip->ili_logged == 0 || XFS_FORCED_SHUTDOWN(ip->i_mount));
|
|
|
|
|
|
|
|
spin_unlock(&lip->li_ailp->xa_lock);
|
|
|
|
|
|
|
|
error = xfs_iflush(ip, &bp);
|
|
|
|
if (!error) {
|
|
|
|
if (!xfs_buf_delwri_queue(bp, buffer_list))
|
|
|
|
rval = XFS_ITEM_FLUSHING;
|
|
|
|
xfs_buf_relse(bp);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
xfs: on-stack delayed write buffer lists
Queue delwri buffers on a local on-stack list instead of a per-buftarg one,
and write back the buffers per-process instead of by waking up xfsbufd.
This is now easily doable given that we have very few places left that write
delwri buffers:
- log recovery:
Only done at mount time, and already forcing out the buffers
synchronously using xfs_flush_buftarg
- quotacheck:
Same story.
- dquot reclaim:
Writes out dirty dquots on the LRU under memory pressure. We might
want to look into doing more of this via xfsaild, but it's already
more optimal than the synchronous inode reclaim that writes each
buffer synchronously.
- xfsaild:
This is the main beneficiary of the change. By keeping a local list
of buffers to write we reduce latency of writing out buffers, and
more importably we can remove all the delwri list promotions which
were hitting the buffer cache hard under sustained metadata loads.
The implementation is very straight forward - xfs_buf_delwri_queue now gets
a new list_head pointer that it adds the delwri buffers to, and all callers
need to eventually submit the list using xfs_buf_delwi_submit or
xfs_buf_delwi_submit_nowait. Buffers that already are on a delwri list are
skipped in xfs_buf_delwri_queue, assuming they already are on another delwri
list. The biggest change to pass down the buffer list was done to the AIL
pushing. Now that we operate on buffers the trylock, push and pushbuf log
item methods are merged into a single push routine, which tries to lock the
item, and if possible add the buffer that needs writeback to the buffer list.
This leads to much simpler code than the previous split but requires the
individual IOP_PUSH instances to unlock and reacquire the AIL around calls
to blocking routines.
Given that xfsailds now also handle writing out buffers, the conditions for
log forcing and the sleep times needed some small changes. The most
important one is that we consider an AIL busy as long we still have buffers
to push, and the other one is that we do increment the pushed LSN for
buffers that are under flushing at this moment, but still count them towards
the stuck items for restart purposes. Without this we could hammer on stuck
items without ever forcing the log and not make progress under heavy random
delete workloads on fast flash storage devices.
[ Dave Chinner:
- rebase on previous patches.
- improved comments for XBF_DELWRI_Q handling
- fix XBF_ASYNC handling in queue submission (test 106 failure)
- rename delwri submit function buffer list parameters for clarity
- xfs_efd_item_push() should return XFS_ITEM_PINNED ]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-04-23 13:58:39 +08:00
|
|
|
|
|
|
|
spin_lock(&lip->li_ailp->xa_lock);
|
|
|
|
out_unlock:
|
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
return rval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Unlock the inode associated with the inode log item.
|
|
|
|
* Clear the fields of the inode and inode log item that
|
|
|
|
* are specific to the current transaction. If the
|
|
|
|
* hold flags is set, do not unlock the inode.
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_unlock(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_log_item *lip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
2010-06-24 09:36:58 +08:00
|
|
|
unsigned short lock_flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-07-08 20:34:47 +08:00
|
|
|
ASSERT(ip->i_itemp != NULL);
|
|
|
|
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-24 09:36:58 +08:00
|
|
|
lock_flags = iip->ili_lock_flags;
|
|
|
|
iip->ili_lock_flags = 0;
|
2011-09-19 23:00:54 +08:00
|
|
|
if (lock_flags)
|
2011-07-08 20:34:47 +08:00
|
|
|
xfs_iunlock(ip, lock_flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-11-30 12:15:46 +08:00
|
|
|
* This is called to find out where the oldest active copy of the inode log
|
|
|
|
* item in the on disk log resides now that the last log write of it completed
|
|
|
|
* at the given lsn. Since we always re-log all dirty data in an inode, the
|
|
|
|
* latest copy in the on disk log is the only one that matters. Therefore,
|
|
|
|
* simply return the given lsn.
|
|
|
|
*
|
|
|
|
* If the inode has been marked stale because the cluster is being freed, we
|
|
|
|
* don't want to (re-)insert this inode into the AIL. There is a race condition
|
|
|
|
* where the cluster buffer may be unpinned before the inode is inserted into
|
|
|
|
* the AIL during transaction committed processing. If the buffer is unpinned
|
|
|
|
* before the inode item has been committed and inserted, then it is possible
|
xfs: unpin stale inodes directly in IOP_COMMITTED
When inodes are marked stale in a transaction, they are treated
specially when the inode log item is being inserted into the AIL.
It tries to avoid moving the log item forward in the AIL due to a
race condition with the writing the underlying buffer back to disk.
The was "fixed" in commit de25c18 ("xfs: avoid moving stale inodes
in the AIL").
To avoid moving the item forward, we return a LSN smaller than the
commit_lsn of the completing transaction, thereby trying to trick
the commit code into not moving the inode forward at all. I'm not
sure this ever worked as intended - it assumes the inode is already
in the AIL, but I don't think the returned LSN would have been small
enough to prevent moving the inode. It appears that the reason it
worked is that the lower LSN of the inodes meant they were inserted
into the AIL and flushed before the inode buffer (which was moved to
the commit_lsn of the transaction).
The big problem is that with delayed logging, the returning of the
different LSN means insertion takes the slow, non-bulk path. Worse
yet is that insertion is to a position -before- the commit_lsn so it
is doing a AIL traversal on every insertion, and has to walk over
all the items that have already been inserted into the AIL. It's
expensive.
To compound the matter further, with delayed logging inodes are
likely to go from clean to stale in a single checkpoint, which means
they aren't even in the AIL at all when we come across them at AIL
insertion time. Hence these were all getting inserted into the AIL
when they simply do not need to be as inodes marked XFS_ISTALE are
never written back.
Transactional/recovery integrity is maintained in this case by the
other items in the unlink transaction that were modified (e.g. the
AGI btree blocks) and committed in the same checkpoint.
So to fix this, simply unpin the stale inodes directly in
xfs_inode_item_committed() and return -1 to indicate that the AIL
insertion code does not need to do any further processing of these
inodes.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2011-07-04 13:27:36 +08:00
|
|
|
* for the buffer to be written and IO completes before the inode is inserted
|
2010-11-30 12:15:46 +08:00
|
|
|
* into the AIL. In that case, we'd be inserting a clean, stale inode into the
|
|
|
|
* AIL which will never get removed. It will, however, get reclaimed which
|
|
|
|
* triggers an assert in xfs_inode_free() complaining about freein an inode
|
|
|
|
* still in the AIL.
|
|
|
|
*
|
xfs: unpin stale inodes directly in IOP_COMMITTED
When inodes are marked stale in a transaction, they are treated
specially when the inode log item is being inserted into the AIL.
It tries to avoid moving the log item forward in the AIL due to a
race condition with the writing the underlying buffer back to disk.
The was "fixed" in commit de25c18 ("xfs: avoid moving stale inodes
in the AIL").
To avoid moving the item forward, we return a LSN smaller than the
commit_lsn of the completing transaction, thereby trying to trick
the commit code into not moving the inode forward at all. I'm not
sure this ever worked as intended - it assumes the inode is already
in the AIL, but I don't think the returned LSN would have been small
enough to prevent moving the inode. It appears that the reason it
worked is that the lower LSN of the inodes meant they were inserted
into the AIL and flushed before the inode buffer (which was moved to
the commit_lsn of the transaction).
The big problem is that with delayed logging, the returning of the
different LSN means insertion takes the slow, non-bulk path. Worse
yet is that insertion is to a position -before- the commit_lsn so it
is doing a AIL traversal on every insertion, and has to walk over
all the items that have already been inserted into the AIL. It's
expensive.
To compound the matter further, with delayed logging inodes are
likely to go from clean to stale in a single checkpoint, which means
they aren't even in the AIL at all when we come across them at AIL
insertion time. Hence these were all getting inserted into the AIL
when they simply do not need to be as inodes marked XFS_ISTALE are
never written back.
Transactional/recovery integrity is maintained in this case by the
other items in the unlink transaction that were modified (e.g. the
AGI btree blocks) and committed in the same checkpoint.
So to fix this, simply unpin the stale inodes directly in
xfs_inode_item_committed() and return -1 to indicate that the AIL
insertion code does not need to do any further processing of these
inodes.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2011-07-04 13:27:36 +08:00
|
|
|
* To avoid this, just unpin the inode directly and return a LSN of -1 so the
|
|
|
|
* transaction committed code knows that it does not need to do any further
|
|
|
|
* processing on the item.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
STATIC xfs_lsn_t
|
|
|
|
xfs_inode_item_committed(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_log_item *lip,
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_lsn_t lsn)
|
|
|
|
{
|
2010-11-30 12:15:46 +08:00
|
|
|
struct xfs_inode_log_item *iip = INODE_ITEM(lip);
|
|
|
|
struct xfs_inode *ip = iip->ili_inode;
|
|
|
|
|
xfs: unpin stale inodes directly in IOP_COMMITTED
When inodes are marked stale in a transaction, they are treated
specially when the inode log item is being inserted into the AIL.
It tries to avoid moving the log item forward in the AIL due to a
race condition with the writing the underlying buffer back to disk.
The was "fixed" in commit de25c18 ("xfs: avoid moving stale inodes
in the AIL").
To avoid moving the item forward, we return a LSN smaller than the
commit_lsn of the completing transaction, thereby trying to trick
the commit code into not moving the inode forward at all. I'm not
sure this ever worked as intended - it assumes the inode is already
in the AIL, but I don't think the returned LSN would have been small
enough to prevent moving the inode. It appears that the reason it
worked is that the lower LSN of the inodes meant they were inserted
into the AIL and flushed before the inode buffer (which was moved to
the commit_lsn of the transaction).
The big problem is that with delayed logging, the returning of the
different LSN means insertion takes the slow, non-bulk path. Worse
yet is that insertion is to a position -before- the commit_lsn so it
is doing a AIL traversal on every insertion, and has to walk over
all the items that have already been inserted into the AIL. It's
expensive.
To compound the matter further, with delayed logging inodes are
likely to go from clean to stale in a single checkpoint, which means
they aren't even in the AIL at all when we come across them at AIL
insertion time. Hence these were all getting inserted into the AIL
when they simply do not need to be as inodes marked XFS_ISTALE are
never written back.
Transactional/recovery integrity is maintained in this case by the
other items in the unlink transaction that were modified (e.g. the
AGI btree blocks) and committed in the same checkpoint.
So to fix this, simply unpin the stale inodes directly in
xfs_inode_item_committed() and return -1 to indicate that the AIL
insertion code does not need to do any further processing of these
inodes.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2011-07-04 13:27:36 +08:00
|
|
|
if (xfs_iflags_test(ip, XFS_ISTALE)) {
|
|
|
|
xfs_inode_item_unpin(lip, 0);
|
|
|
|
return -1;
|
|
|
|
}
|
2010-06-23 16:11:15 +08:00
|
|
|
return lsn;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX rcc - this one really has to do something. Probably needs
|
|
|
|
* to stamp in a new field in the incore inode.
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xfs_inode_item_committing(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_log_item *lip,
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_lsn_t lsn)
|
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
INODE_ITEM(lip)->ili_last_lsn = lsn;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the ops vector shared by all buf log items.
|
|
|
|
*/
|
2011-10-28 17:54:24 +08:00
|
|
|
static const struct xfs_item_ops xfs_inode_item_ops = {
|
2010-06-23 16:11:15 +08:00
|
|
|
.iop_size = xfs_inode_item_size,
|
|
|
|
.iop_format = xfs_inode_item_format,
|
|
|
|
.iop_pin = xfs_inode_item_pin,
|
|
|
|
.iop_unpin = xfs_inode_item_unpin,
|
|
|
|
.iop_unlock = xfs_inode_item_unlock,
|
|
|
|
.iop_committed = xfs_inode_item_committed,
|
|
|
|
.iop_push = xfs_inode_item_push,
|
|
|
|
.iop_committing = xfs_inode_item_committing
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the inode log item for a newly allocated (in-core) inode.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xfs_inode_item_init(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct xfs_mount *mp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_inode_log_item *iip;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ASSERT(ip->i_itemp == NULL);
|
|
|
|
iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, KM_SLEEP);
|
|
|
|
|
|
|
|
iip->ili_inode = ip;
|
2010-03-23 07:10:00 +08:00
|
|
|
xfs_log_item_init(mp, &iip->ili_item, XFS_LI_INODE,
|
|
|
|
&xfs_inode_item_ops);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the inode log item and any memory hanging off of it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xfs_inode_item_destroy(
|
|
|
|
xfs_inode_t *ip)
|
|
|
|
{
|
xfs: allocate log vector buffers outside CIL context lock
One of the problems we currently have with delayed logging is that
under serious memory pressure we can deadlock memory reclaim. THis
occurs when memory reclaim (such as run by kswapd) is reclaiming XFS
inodes and issues a log force to unpin inodes that are dirty in the
CIL.
The CIL is pushed, but this will only occur once it gets the CIL
context lock to ensure that all committing transactions are complete
and no new transactions start being committed to the CIL while the
push switches to a new context.
The deadlock occurs when the CIL context lock is held by a
committing process that is doing memory allocation for log vector
buffers, and that allocation is then blocked on memory reclaim
making progress. Memory reclaim, however, is blocked waiting for
a log force to make progress, and so we effectively deadlock at this
point.
To solve this problem, we have to move the CIL log vector buffer
allocation outside of the context lock so that memory reclaim can
always make progress when it needs to force the log. The problem
with doing this is that a CIL push can take place while we are
determining if we need to allocate a new log vector buffer for
an item and hence the current log vector may go away without
warning. That means we canot rely on the existing log vector being
present when we finally grab the context lock and so we must have a
replacement buffer ready to go at all times.
To ensure this, introduce a "shadow log vector" buffer that is
always guaranteed to be present when we gain the CIL context lock
and format the item. This shadow buffer may or may not be used
during the formatting, but if the log item does not have an existing
log vector buffer or that buffer is too small for the new
modifications, we swap it for the new shadow buffer and format
the modifications into that new log vector buffer.
The result of this is that for any object we modify more than once
in a given CIL checkpoint, we double the memory required
to track dirty regions in the log. For single modifications then
we consume the shadow log vectorwe allocate on commit, and that gets
consumed by the checkpoint. However, if we make multiple
modifications, then the second transaction commit will allocate a
shadow log vector and hence we will end up with double the memory
usage as only one of the log vectors is consumed by the CIL
checkpoint. The remaining shadow vector will be freed when th elog
item is freed.
This can probably be optimised in future - access to the shadow log
vector is serialised by the object lock (as opposited to the active
log vector, which is controlled by the CIL context lock) and so we
can probably free shadow log vector from some objects when the log
item is marked clean on removal from the AIL.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2016-07-22 07:52:35 +08:00
|
|
|
kmem_free(ip->i_itemp->ili_item.li_lv_shadow);
|
2005-04-17 06:20:36 +08:00
|
|
|
kmem_zone_free(xfs_ili_zone, ip->i_itemp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the inode flushing I/O completion routine. It is called
|
|
|
|
* from interrupt level when the buffer containing the inode is
|
|
|
|
* flushed to disk. It is responsible for removing the inode item
|
|
|
|
* from the AIL if it has not been re-logged, and unlocking the inode's
|
|
|
|
* flush lock.
|
2010-12-20 09:03:17 +08:00
|
|
|
*
|
|
|
|
* To reduce AIL lock traffic as much as possible, we scan the buffer log item
|
|
|
|
* list for other inodes that will run this function. We remove them from the
|
|
|
|
* buffer list so we can process all the inode IO completions in one AIL lock
|
|
|
|
* traversal.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
xfs_iflush_done(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct xfs_log_item *lip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-12-20 09:03:17 +08:00
|
|
|
struct xfs_inode_log_item *iip;
|
|
|
|
struct xfs_log_item *blip;
|
|
|
|
struct xfs_log_item *next;
|
|
|
|
struct xfs_log_item *prev;
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_ail *ailp = lip->li_ailp;
|
2010-12-20 09:03:17 +08:00
|
|
|
int need_ail = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scan the buffer IO completions for other inodes being completed and
|
|
|
|
* attach them to the current inode log item.
|
|
|
|
*/
|
2011-07-13 19:43:49 +08:00
|
|
|
blip = bp->b_fspriv;
|
2010-12-20 09:03:17 +08:00
|
|
|
prev = NULL;
|
|
|
|
while (blip != NULL) {
|
2014-10-03 07:09:50 +08:00
|
|
|
if (blip->li_cb != xfs_iflush_done) {
|
2010-12-20 09:03:17 +08:00
|
|
|
prev = blip;
|
|
|
|
blip = blip->li_bio_list;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove from list */
|
|
|
|
next = blip->li_bio_list;
|
|
|
|
if (!prev) {
|
2011-07-13 19:43:49 +08:00
|
|
|
bp->b_fspriv = next;
|
2010-12-20 09:03:17 +08:00
|
|
|
} else {
|
|
|
|
prev->li_bio_list = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add to current list */
|
|
|
|
blip->li_bio_list = lip->li_bio_list;
|
|
|
|
lip->li_bio_list = blip;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* while we have the item, do the unlocked check for needing
|
|
|
|
* the AIL lock.
|
|
|
|
*/
|
|
|
|
iip = INODE_ITEM(blip);
|
|
|
|
if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn)
|
|
|
|
need_ail++;
|
|
|
|
|
|
|
|
blip = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure we capture the state of the initial inode. */
|
|
|
|
iip = INODE_ITEM(lip);
|
|
|
|
if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn)
|
|
|
|
need_ail++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We only want to pull the item from the AIL if it is
|
|
|
|
* actually there and its location in the log has not
|
|
|
|
* changed since we started the flush. Thus, we only bother
|
|
|
|
* if the ili_logged flag is set and the inode's lsn has not
|
|
|
|
* changed. First we check the lsn outside
|
|
|
|
* the lock since it's cheaper, and then we recheck while
|
|
|
|
* holding the lock before removing the inode from the AIL.
|
|
|
|
*/
|
2010-12-20 09:03:17 +08:00
|
|
|
if (need_ail) {
|
|
|
|
struct xfs_log_item *log_items[need_ail];
|
|
|
|
int i = 0;
|
2008-10-30 14:39:58 +08:00
|
|
|
spin_lock(&ailp->xa_lock);
|
2010-12-20 09:03:17 +08:00
|
|
|
for (blip = lip; blip; blip = blip->li_bio_list) {
|
|
|
|
iip = INODE_ITEM(blip);
|
|
|
|
if (iip->ili_logged &&
|
|
|
|
blip->li_lsn == iip->ili_flush_lsn) {
|
|
|
|
log_items[i++] = blip;
|
|
|
|
}
|
|
|
|
ASSERT(i <= need_ail);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-12-20 09:03:17 +08:00
|
|
|
/* xfs_trans_ail_delete_bulk() drops the AIL lock. */
|
2012-04-23 13:58:41 +08:00
|
|
|
xfs_trans_ail_delete_bulk(ailp, log_items, i,
|
|
|
|
SHUTDOWN_CORRUPT_INCORE);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2010-12-20 09:03:17 +08:00
|
|
|
* clean up and unlock the flush lock now we are done. We can clear the
|
|
|
|
* ili_last_fields bits now that we know that the data corresponding to
|
|
|
|
* them is safely on disk.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2010-12-20 09:03:17 +08:00
|
|
|
for (blip = lip; blip; blip = next) {
|
|
|
|
next = blip->li_bio_list;
|
|
|
|
blip->li_bio_list = NULL;
|
|
|
|
|
|
|
|
iip = INODE_ITEM(blip);
|
|
|
|
iip->ili_logged = 0;
|
|
|
|
iip->ili_last_fields = 0;
|
|
|
|
xfs_ifunlock(iip->ili_inode);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-04-23 13:58:41 +08:00
|
|
|
* This is the inode flushing abort routine. It is called from xfs_iflush when
|
|
|
|
* the filesystem is shutting down to clean up the inode state. It is
|
|
|
|
* responsible for removing the inode item from the AIL if it has not been
|
|
|
|
* re-logged, and unlocking the inode's flush lock.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
xfs_iflush_abort(
|
2012-04-23 13:58:41 +08:00
|
|
|
xfs_inode_t *ip,
|
|
|
|
bool stale)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-10-30 14:39:58 +08:00
|
|
|
xfs_inode_log_item_t *iip = ip->i_itemp;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (iip) {
|
|
|
|
if (iip->ili_item.li_flags & XFS_LI_IN_AIL) {
|
2015-08-19 08:01:08 +08:00
|
|
|
xfs_trans_ail_remove(&iip->ili_item,
|
|
|
|
stale ? SHUTDOWN_LOG_IO_ERROR :
|
2012-04-23 13:58:41 +08:00
|
|
|
SHUTDOWN_CORRUPT_INCORE);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
iip->ili_logged = 0;
|
|
|
|
/*
|
|
|
|
* Clear the ili_last_fields bits now that we know that the
|
|
|
|
* data corresponding to them is safely on disk.
|
|
|
|
*/
|
|
|
|
iip->ili_last_fields = 0;
|
|
|
|
/*
|
|
|
|
* Clear the inode logging fields so no more flushes are
|
|
|
|
* attempted.
|
|
|
|
*/
|
2012-02-29 17:53:54 +08:00
|
|
|
iip->ili_fields = 0;
|
xfs: optimise away log forces on timestamp updates for fdatasync
xfs: timestamp updates cause excessive fdatasync log traffic
Sage Weil reported that a ceph test workload was writing to the
log on every fdatasync during an overwrite workload. Event tracing
showed that the only metadata modification being made was the
timestamp updates during the write(2) syscall, but fdatasync(2)
is supposed to ignore them. The key observation was that the
transactions in the log all looked like this:
INODE: #regs: 4 ino: 0x8b flags: 0x45 dsize: 32
And contained a flags field of 0x45 or 0x85, and had data and
attribute forks following the inode core. This means that the
timestamp updates were triggering dirty relogging of previously
logged parts of the inode that hadn't yet been flushed back to
disk.
There are two parts to this problem. The first is that XFS relogs
dirty regions in subsequent transactions, so it carries around the
fields that have been dirtied since the last time the inode was
written back to disk, not since the last time the inode was forced
into the log.
The second part is that on v5 filesystems, the inode change count
update during inode dirtying also sets the XFS_ILOG_CORE flag, so
on v5 filesystems this makes a timestamp update dirty the entire
inode.
As a result when fdatasync is run, it looks at the dirty fields in
the inode, and sees more than just the timestamp flag, even though
the only metadata change since the last fdatasync was just the
timestamps. Hence we force the log on every subsequent fdatasync
even though it is not needed.
To fix this, add a new field to the inode log item that tracks
changes since the last time fsync/fdatasync forced the log to flush
the changes to the journal. This flag is updated when we dirty the
inode, but we do it before updating the change count so it does not
carry the "core dirty" flag from timestamp updates. The fields are
zeroed when the inode is marked clean (due to writeback/freeing) or
when an fsync/datasync forces the log. Hence if we only dirty the
timestamps on the inode between fsync/fdatasync calls, the fdatasync
will not trigger another log force.
Over 100 runs of the test program:
Ext4 baseline:
runtime: 1.63s +/- 0.24s
avg lat: 1.59ms +/- 0.24ms
iops: ~2000
XFS, vanilla kernel:
runtime: 2.45s +/- 0.18s
avg lat: 2.39ms +/- 0.18ms
log forces: ~400/s
iops: ~1000
XFS, patched kernel:
runtime: 1.49s +/- 0.26s
avg lat: 1.46ms +/- 0.25ms
log forces: ~30/s
iops: ~1500
Reported-by: Sage Weil <sage@redhat.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-11-03 10:14:59 +08:00
|
|
|
iip->ili_fsync_fields = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Release the inode's flush lock since we're done with it.
|
|
|
|
*/
|
|
|
|
xfs_ifunlock(ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
xfs_istale_done(
|
2010-06-23 16:11:15 +08:00
|
|
|
struct xfs_buf *bp,
|
|
|
|
struct xfs_log_item *lip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-04-23 13:58:41 +08:00
|
|
|
xfs_iflush_abort(INODE_ITEM(lip)->ili_inode, true);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-06-09 12:55:38 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* convert an xfs_inode_log_format struct from either 32 or 64 bit versions
|
|
|
|
* (which can have different field alignments) to the native version
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_inode_item_format_convert(
|
|
|
|
xfs_log_iovec_t *buf,
|
|
|
|
xfs_inode_log_format_t *in_f)
|
|
|
|
{
|
|
|
|
if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) {
|
2010-06-23 16:11:15 +08:00
|
|
|
xfs_inode_log_format_32_t *in_f32 = buf->i_addr;
|
2006-06-09 12:55:38 +08:00
|
|
|
|
|
|
|
in_f->ilf_type = in_f32->ilf_type;
|
|
|
|
in_f->ilf_size = in_f32->ilf_size;
|
|
|
|
in_f->ilf_fields = in_f32->ilf_fields;
|
|
|
|
in_f->ilf_asize = in_f32->ilf_asize;
|
|
|
|
in_f->ilf_dsize = in_f32->ilf_dsize;
|
|
|
|
in_f->ilf_ino = in_f32->ilf_ino;
|
|
|
|
/* copy biggest field of ilf_u */
|
|
|
|
memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
|
|
|
|
in_f32->ilf_u.ilfu_uuid.__u_bits,
|
|
|
|
sizeof(uuid_t));
|
|
|
|
in_f->ilf_blkno = in_f32->ilf_blkno;
|
|
|
|
in_f->ilf_len = in_f32->ilf_len;
|
|
|
|
in_f->ilf_boffset = in_f32->ilf_boffset;
|
|
|
|
return 0;
|
|
|
|
} else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){
|
2010-06-23 16:11:15 +08:00
|
|
|
xfs_inode_log_format_64_t *in_f64 = buf->i_addr;
|
2006-06-09 12:55:38 +08:00
|
|
|
|
|
|
|
in_f->ilf_type = in_f64->ilf_type;
|
|
|
|
in_f->ilf_size = in_f64->ilf_size;
|
|
|
|
in_f->ilf_fields = in_f64->ilf_fields;
|
|
|
|
in_f->ilf_asize = in_f64->ilf_asize;
|
|
|
|
in_f->ilf_dsize = in_f64->ilf_dsize;
|
|
|
|
in_f->ilf_ino = in_f64->ilf_ino;
|
|
|
|
/* copy biggest field of ilf_u */
|
|
|
|
memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
|
|
|
|
in_f64->ilf_u.ilfu_uuid.__u_bits,
|
|
|
|
sizeof(uuid_t));
|
|
|
|
in_f->ilf_blkno = in_f64->ilf_blkno;
|
|
|
|
in_f->ilf_len = in_f64->ilf_len;
|
|
|
|
in_f->ilf_boffset = in_f64->ilf_boffset;
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-25 12:58:08 +08:00
|
|
|
return -EFSCORRUPTED;
|
2006-06-09 12:55:38 +08:00
|
|
|
}
|