Re: [PATCH 2/7] xfs: clean up the EFI and EFD log format handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Apr 19, 2021 at 10:27:59AM +0200, Christoph Hellwig wrote:
> The extent structure embedded into the EFI and EFD items was originally
> defined as a structure with implicit padding, which makes it a mess
> to handle between 32-bit and 64-bit kernels as the 32-bit ABI packs them
> tight, while the 64-bit ABI implicitly adds an implicit 4-bye padding.
> Log recovery has been able to deal with both formats for a long time,
> although in a rather messy way where the default definition varies
> between the ABIs, but log recovery has two extra special cases for
> padded or unpadded variants.
> 
> Change this to always write the properly fully padded EFI and EFD
> structures to the log, and only special case the unpadded one during
> recovery.

Hmm... so the behavior change here is that 32-bit kernels will start
logging 16-byte xfs_extent structures (like 64-bit kernels)?  I see that
xfs_extent_32 was added for 2.6.18; won't this break recovery on
everything from before that?

Granted, 2.6.17 came out 15 years ago and the last 2.6.16 LTS kernel was
released in 2008 so maybe we don't care, but this would seem to be a
breaking change, right?  This seems like a reasonable change for all V5
filesystems (since that format emerged well after 2.6.18), but not so
good for V4.

--D

> Signed-off-by: Christoph Hellwig <hch@xxxxxx>
> ---
>  fs/xfs/libxfs/xfs_log_format.h |  49 ++++++--------
>  fs/xfs/xfs_extfree_item.c      | 115 ++++++++++++++-------------------
>  fs/xfs/xfs_extfree_item.h      |   2 +-
>  fs/xfs/xfs_ondisk.h            |   5 +-
>  4 files changed, 71 insertions(+), 100 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
> index ea0fe9f121adff..639035052b4f65 100644
> --- a/fs/xfs/libxfs/xfs_log_format.h
> +++ b/fs/xfs/libxfs/xfs_log_format.h
> @@ -542,56 +542,43 @@ xfs_blft_from_flags(struct xfs_buf_log_format *blf)
>  /*
>   * EFI/EFD log format definitions
>   */
> -typedef struct xfs_extent {
> -	xfs_fsblock_t	ext_start;
> -	xfs_extlen_t	ext_len;
> -} xfs_extent_t;
>  
> -/*
> - * Since an xfs_extent_t has types (start:64, len: 32)
> - * there are different alignments on 32 bit and 64 bit kernels.
> - * So we provide the different variants for use by a
> - * conversion routine.
> - */
> -typedef struct xfs_extent_32 {
> -	uint64_t	ext_start;
> -	uint32_t	ext_len;
> -} __attribute__((packed)) xfs_extent_32_t;
> -
> -typedef struct xfs_extent_64 {
> +struct xfs_extent {
>  	uint64_t	ext_start;
>  	uint32_t	ext_len;
>  	uint32_t	ext_pad;
> -} xfs_extent_64_t;
> +};
>  
>  /*
>   * This is the structure used to lay out an efi log item in the
>   * log.  The efi_extents field is a variable size array whose
>   * size is given by efi_nextents.
>   */
> -typedef struct xfs_efi_log_format {
> +struct xfs_efi_log_format {
>  	uint16_t		efi_type;	/* efi log item type */
>  	uint16_t		efi_size;	/* size of this item */
>  	uint32_t		efi_nextents;	/* # extents to free */
>  	uint64_t		efi_id;		/* efi identifier */
> -	xfs_extent_t		efi_extents[1];	/* array of extents to free */
> -} xfs_efi_log_format_t;
> +	struct xfs_extent	efi_extents[1];	/* array of extents to free */
> +};
>  
> -typedef struct xfs_efi_log_format_32 {
> -	uint16_t		efi_type;	/* efi log item type */
> -	uint16_t		efi_size;	/* size of this item */
> -	uint32_t		efi_nextents;	/* # extents to free */
> -	uint64_t		efi_id;		/* efi identifier */
> -	xfs_extent_32_t		efi_extents[1];	/* array of extents to free */
> -} __attribute__((packed)) xfs_efi_log_format_32_t;
> +/*
> + * Version of the xfs_extent and xfs_efi_log_format structures that do not
> + * contain padding.  These used to be written to the log by older 32-bit kernels
> + * and will be dealt with transparently by log recovery.
> + */
> +struct xfs_extent_32 {
> +	uint64_t	ext_start;
> +	uint32_t	ext_len;
> +} __attribute__((packed));
>  
> -typedef struct xfs_efi_log_format_64 {
> +struct xfs_efi_log_format_32 {
>  	uint16_t		efi_type;	/* efi log item type */
>  	uint16_t		efi_size;	/* size of this item */
>  	uint32_t		efi_nextents;	/* # extents to free */
>  	uint64_t		efi_id;		/* efi identifier */
> -	xfs_extent_64_t		efi_extents[1];	/* array of extents to free */
> -} xfs_efi_log_format_64_t;
> +	struct xfs_extent_32	efi_extents[1];	/* array of extents to free */
> +} __attribute__((packed));
>  
>  /*
>   * This is the structure used to lay out an efd log item in the
> @@ -603,7 +590,7 @@ struct xfs_efd_log_format {
>  	uint16_t		efd_size;	/* size of this item */
>  	uint32_t		efd_nextents;	/* # of extents freed */
>  	uint64_t		efd_efi_id;	/* id of corresponding efi */
> -	xfs_extent_t		efd_extents[1];	/* array of extents freed */
> +	struct xfs_extent	efd_extents[1];	/* array of extents freed */
>  };
>  
>  /*
> diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
> index ac17fdb9283489..ed8d0790908ea7 100644
> --- a/fs/xfs/xfs_extfree_item.c
> +++ b/fs/xfs/xfs_extfree_item.c
> @@ -74,7 +74,7 @@ xfs_efi_item_sizeof(
>  	struct xfs_efi_log_item *efip)
>  {
>  	return sizeof(struct xfs_efi_log_format) +
> -	       (efip->efi_format.efi_nextents - 1) * sizeof(xfs_extent_t);
> +	       (efip->efi_format.efi_nextents - 1) * sizeof(struct xfs_extent);
>  }
>  
>  STATIC void
> @@ -158,7 +158,7 @@ xfs_efi_init(
>  	ASSERT(nextents > 0);
>  	if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
>  		size = (uint)(sizeof(struct xfs_efi_log_item) +
> -			((nextents - 1) * sizeof(xfs_extent_t)));
> +			((nextents - 1) * sizeof(struct xfs_extent)));
>  		efip = kmem_zalloc(size, 0);
>  	} else {
>  		efip = kmem_cache_zalloc(xfs_efi_zone,
> @@ -174,61 +174,6 @@ xfs_efi_init(
>  	return efip;
>  }
>  
> -/*
> - * Copy an EFI format buffer from the given buf, and into the destination
> - * EFI format structure.
> - * The given buffer can be in 32 bit or 64 bit form (which has different padding),
> - * one of which will be the native format for this kernel.
> - * It will handle the conversion of formats if necessary.
> - */
> -STATIC int
> -xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
> -{
> -	xfs_efi_log_format_t *src_efi_fmt = buf->i_addr;
> -	uint i;
> -	uint len = sizeof(xfs_efi_log_format_t) + 
> -		(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t);  
> -	uint len32 = sizeof(xfs_efi_log_format_32_t) + 
> -		(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t);  
> -	uint len64 = sizeof(xfs_efi_log_format_64_t) + 
> -		(src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t);  
> -
> -	if (buf->i_len == len) {
> -		memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len);
> -		return 0;
> -	} else if (buf->i_len == len32) {
> -		xfs_efi_log_format_32_t *src_efi_fmt_32 = buf->i_addr;
> -
> -		dst_efi_fmt->efi_type     = src_efi_fmt_32->efi_type;
> -		dst_efi_fmt->efi_size     = src_efi_fmt_32->efi_size;
> -		dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents;
> -		dst_efi_fmt->efi_id       = src_efi_fmt_32->efi_id;
> -		for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
> -			dst_efi_fmt->efi_extents[i].ext_start =
> -				src_efi_fmt_32->efi_extents[i].ext_start;
> -			dst_efi_fmt->efi_extents[i].ext_len =
> -				src_efi_fmt_32->efi_extents[i].ext_len;
> -		}
> -		return 0;
> -	} else if (buf->i_len == len64) {
> -		xfs_efi_log_format_64_t *src_efi_fmt_64 = buf->i_addr;
> -
> -		dst_efi_fmt->efi_type     = src_efi_fmt_64->efi_type;
> -		dst_efi_fmt->efi_size     = src_efi_fmt_64->efi_size;
> -		dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents;
> -		dst_efi_fmt->efi_id       = src_efi_fmt_64->efi_id;
> -		for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
> -			dst_efi_fmt->efi_extents[i].ext_start =
> -				src_efi_fmt_64->efi_extents[i].ext_start;
> -			dst_efi_fmt->efi_extents[i].ext_len =
> -				src_efi_fmt_64->efi_extents[i].ext_len;
> -		}
> -		return 0;
> -	}
> -	XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
> -	return -EFSCORRUPTED;
> -}
> -
>  static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)
>  {
>  	return container_of(lip, struct xfs_efd_log_item, efd_item);
> @@ -254,7 +199,7 @@ xfs_efd_item_sizeof(
>  	struct xfs_efd_log_item *efdp)
>  {
>  	return sizeof(struct xfs_efd_log_format) +
> -	       (efdp->efd_format.efd_nextents - 1) * sizeof(xfs_extent_t);
> +	       (efdp->efd_format.efd_nextents - 1) * sizeof(struct xfs_extent);
>  }
>  
>  STATIC void
> @@ -687,6 +632,36 @@ static const struct xfs_item_ops xfs_efi_item_ops = {
>  	.iop_relog	= xfs_efi_item_relog,
>  };
>  
> +/*
> + * Convert from an unpadded EFI log item written by old 32-bit kernels to the
> + * proper format.
> + */
> +static int
> +xfs_efi_copy_format_32(
> +	struct xfs_efi_log_format	*dst,
> +	struct xfs_log_iovec		*buf)
> +{
> +	struct xfs_efi_log_format_32	*src = buf->i_addr;
> +	unsigned int			i;
> +
> +	if (buf->i_len != sizeof(*src) +
> +	    (src->efi_nextents - 1) * sizeof(struct xfs_extent_32)) {
> +		XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, NULL);
> +		return -EFSCORRUPTED;
> +	}
> +
> +	dst->efi_type = src->efi_type;
> +	dst->efi_size = src->efi_size;
> +	dst->efi_nextents = src->efi_nextents;
> +	dst->efi_id = src->efi_id;
> +	for (i = 0; i < dst->efi_nextents; i++) {
> +		dst->efi_extents[i].ext_start = src->efi_extents[i].ext_start;
> +		dst->efi_extents[i].ext_len = src->efi_extents[i].ext_len;
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * This routine is called to create an in-core extent free intent
>   * item from the efi format structure which was logged on disk.
> @@ -703,18 +678,22 @@ xlog_recover_efi_commit_pass2(
>  {
>  	struct xfs_mount		*mp = log->l_mp;
>  	struct xfs_efi_log_item		*efip;
> -	struct xfs_efi_log_format	*efi_formatp;
> +	struct xfs_log_iovec		*buf = &item->ri_buf[0];
> +	struct xfs_efi_log_format	*src = buf->i_addr;
>  	int				error;
>  
> -	efi_formatp = item->ri_buf[0].i_addr;
> +	efip = xfs_efi_init(mp, src->efi_nextents);
>  
> -	efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
> -	error = xfs_efi_copy_format(&item->ri_buf[0], &efip->efi_format);
> -	if (error) {
> -		xfs_efi_item_free(efip);
> -		return error;
> +	if (buf->i_len != sizeof(*src) +
> +	    (src->efi_nextents - 1) * sizeof(struct xfs_extent)) {
> +		error = xfs_efi_copy_format_32(&efip->efi_format, buf);
> +		if (error)
> +			goto out_free_efi;
> +	} else {
> +		memcpy(&efip->efi_format, src, buf->i_len);
>  	}
> -	atomic_set(&efip->efi_next_extent, efi_formatp->efi_nextents);
> +
> +	atomic_set(&efip->efi_next_extent, efip->efi_format.efi_nextents);
>  	/*
>  	 * Insert the intent into the AIL directly and drop one reference so
>  	 * that finishing or canceling the work will drop the other.
> @@ -722,6 +701,10 @@ xlog_recover_efi_commit_pass2(
>  	xfs_trans_ail_insert(log->l_ailp, &efip->efi_item, lsn);
>  	xfs_efi_release(efip);
>  	return 0;
> +
> +out_free_efi:
> +	xfs_efi_item_free(efip);
> +	return error;
>  }
>  
>  const struct xlog_recover_item_ops xlog_efi_item_ops = {
> diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h
> index 6b80452ad2a71b..e09afd0f63ff59 100644
> --- a/fs/xfs/xfs_extfree_item.h
> +++ b/fs/xfs/xfs_extfree_item.h
> @@ -49,7 +49,7 @@ struct xfs_efi_log_item {
>  	struct xfs_log_item	efi_item;
>  	atomic_t		efi_refcount;
>  	atomic_t		efi_next_extent;
> -	xfs_efi_log_format_t	efi_format;
> +	struct xfs_efi_log_format efi_format;
>  };
>  
>  /*
> diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
> index 7328ff92e0ee8a..739476f7dffa21 100644
> --- a/fs/xfs/xfs_ondisk.h
> +++ b/fs/xfs/xfs_ondisk.h
> @@ -118,10 +118,11 @@ xfs_check_ondisk_structs(void)
>  	/* log structures */
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_buf_log_format,	88);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_dq_logformat,		24);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_efd_log_format,	32);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format,	32);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_32,	28);
> -	XFS_CHECK_STRUCT_SIZE(struct xfs_efi_log_format_64,	32);
> +	XFS_CHECK_STRUCT_SIZE(struct xfs_extent,		16);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_extent_32,		12);
> -	XFS_CHECK_STRUCT_SIZE(struct xfs_extent_64,		16);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_log_dinode,		176);
>  	XFS_CHECK_STRUCT_SIZE(struct xfs_icreate_log,		28);
>  	XFS_CHECK_STRUCT_SIZE(xfs_ictimestamp_t,		8);
> -- 
> 2.30.1
> 



[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux