Skip to contentSkip to conversation

xfsprogs: log format fixes, db command

PATCH New Brian Foster 1 participant 5 messages Nov 25, 2015

lore
Share & tools
lore
#0 lore raw

cover xfsprogs: log format fixes, db command

Hi all,

This series includes a few fixes to log formatting/handling code in xfsprogs. Patch 1 fixes a minor issue in xfs_logprint. Patch 2 fixes the incorrect formatting issue with larger lsunit alignments noted in the previously posted torn log write detection series:

http://oss.sgi.com/pipermail/xfs/2015-November/044845.html

Patch 3 is mostly a refactor to allow the log format command to use variable sized records rather than unconditionally use 256kB records. Patch 4 adds the 'logformat' xfs_db command to facilitate testing of the log formatting mechanism.

This has been lightly tested with xfstests, including via a couple forthcoming tests that take advantage of the logformat command. Thoughts, reviews, flames appreciated.

Brian

Brian Foster (4):
logprint: use correct ext. header count for unaligned size
libxfs: format the log with valid log record headers
libxfs: conditionalize log format record size optimization
db: add the logformat command

 copy/xfs_copy.c     |   2 +-
 db/Makefile         |   4 +-
 db/command.c        |   2 +
 db/logformat.c      | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 db/logformat.h      |  19 +++++++
 db/metadump.c       |   2 +-
 db/sb.c             |   2 +-
 include/libxfs.h    |   2 +-
 libxfs/rdwr.c       |  89 ++++++++++++++++++++++++-------
 logprint/log_misc.c |   2 +
 mkfs/xfs_mkfs.c     |   3 +-
 repair/phase2.c     |   2 +-
 repair/xfs_repair.c |   2 +-
 13 files changed, 251 insertions(+), 29 deletions(-)
 create mode 100644 db/logformat.c
 create mode 100644 db/logformat.h
Signature · 6 lines
2.1.0

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#1 lore raw

PATCH 1/4 logprint: use correct ext. header count for unaligned size

A log record has 1 record header and up to 7 additional extended record headers depending on the size of the record. This is required for log record packing. A header is required for each 32k of record data.

The header count for a particular record is fixed, based on the log buffer size (h_size) specified in the record header. logprint calculates the expected extended header count based on h_size, but does not account for a log buffer size not aligned with 32k. This results in spurious invalid header count errors for an otherwise valid log. This can be reproduced by mounting a filesystem with '-o logbsize=16k' and running xfs_logprint after a subsequent unmount.

Update xlog_print_extended_headers() to incorporate a non-32k aligned log buffer size in the expected extended record header count calculation. This is consistent with how the header count is calculated in the kernel.

---
 logprint/log_misc.c | 2 ++
 1 file changed, 2 insertions(+)
logprint/log_misc.c +2 -0
index 4cdcbec..7378fe1 100644
@@ -1294,6 +1294,8 @@ xlog_print_extended_headers(
 
 	num_required = howmany(len, XLOG_HEADER_CYCLE_SIZE);
 	num_hdrs = be32_to_cpu(hdr->h_size) / XLOG_HEADER_CYCLE_SIZE;
+	if (be32_to_cpu(hdr->h_size) % XLOG_HEADER_CYCLE_SIZE)
+		num_hdrs++;
 
 	if (num_required > num_hdrs) {
 	    print_xlog_bad_reqd_hdrs((*blkno)-1, num_required, num_hdrs);
Signature · 6 lines
2.1.0

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#2 lore raw

PATCH 2/4 libxfs: format the log with valid log record headers

In reply to Brian Foster

xfsprogs supports the ability to clear or reformat the log to an arbitrary cycle number. This functionality is invoked in mkfs, xfs_repair, etc. The code currently fills the log with unmount records sized based on the specified log stripe unit alignment. When the stripe unit is sufficiently larger, however, the resulting log records are technically invalid. For example, the log buffer size (h_size) field is always hardcoded to 32k even when the particular record length is larger.

This has not traditionally been a problem because the kernel does not fully process the log record when the log is clean. Recent changes to unconditionally CRC verify the head of the log changes this behavior, however, which means the log should be valid. Technically, the records written from userspace are not written with a CRC, but this also helps support unit testing by writing a log that can be verified with logprint.

Update libxfs_log_header() to write a valid log record based on the specified log stripe unit. h_size is updated to the log buffer size based on the stripe unit and the expected number of extended record headers are written out based on the log buffer size.

---
 libxfs/rdwr.c | 74 +++++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 57 insertions(+), 17 deletions(-)
libxfs/rdwr.c +57 -17
index 0804285..16904d0 100644
@@ -279,6 +279,7 @@ libxfs_log_header(
 	char			*p = caddr;
 	__be32			cycle_lsn;
 	int			i, len;
+	int			hdrs = 1;
 
 	if (lsn == NULLCOMMITLSN)
 		lsn = xlog_assign_lsn(XLOG_INIT_CYCLE, 0);
@@ -291,41 +292,80 @@ libxfs_log_header(
 	head->h_magicno = cpu_to_be32(XLOG_HEADER_MAGIC_NUM);
 	head->h_cycle = cpu_to_be32(CYCLE_LSN(lsn));
 	head->h_version = cpu_to_be32(version);
-	if (len != 1)
-		head->h_len = cpu_to_be32(sunit - BBSIZE);
-	else
-		head->h_len = cpu_to_be32(20);
 	head->h_crc = cpu_to_le32(0);
 	head->h_prev_block = cpu_to_be32(-1);
 	head->h_num_logops = cpu_to_be32(1);
 	head->h_fmt = cpu_to_be32(fmt);
-	head->h_size = cpu_to_be32(XLOG_HEADER_CYCLE_SIZE);
+	head->h_size = cpu_to_be32(MAX(sunit, XLOG_BIG_RECORD_BSIZE));
 
 	head->h_lsn = cpu_to_be64(lsn);
 	head->h_tail_lsn = cpu_to_be64(tail_lsn);
 
 	memcpy(&head->h_fs_uuid, fs_uuid, sizeof(uuid_t));
 
-	p = nextfunc(p, BBSIZE, private);
-	unmount_record(p);
-
 	/*
-	 * The kernel expects to see either a log record header magic or the LSN
-	 * cycle at the top of every log block (for example, see
-	 * xlog_[un]pack_data() and xlog_get_cycle()). Pack the unmount record
-	 * block appropriately here.
+	 * The kernel expects to see either a log record header magic value or
+	 * the LSN cycle at the top of every log block. The first word of each
+	 * non-header block is copied to the record headers and replaced with
+	 * the cycle value (see xlog_[un]pack_data() and xlog_get_cycle() for
+	 * details).
+	 *
+	 * Even though we only ever write an unmount record (one block), we
+	 * support writing log records up to the max log buffer size of 256k to
+	 * improve log format performance. This means a record can require up
+	 * to 8 headers (1 rec. header + 7 ext. headers) for the packed cycle
+	 * data (each header supports 32k of data).
 	 */
 	cycle_lsn = CYCLE_LSN_DISK(head->h_lsn);
+	if (version == 2 && sunit > XLOG_HEADER_CYCLE_SIZE) {
+		hdrs = sunit / XLOG_HEADER_CYCLE_SIZE;
+		if (sunit % XLOG_HEADER_CYCLE_SIZE)
+			hdrs++;
+	}
+
+	/*
+	 * A fixed number of extended headers is expected based on h_size. If
+	 * required, format those now so the unmount record is located
+	 * correctly.
+	 *
+	 * Since we only write an unmount record, we only need one h_cycle_data
+	 * entry for the unmount record block. The subsequent record data
+	 * blocks are zeroed, which means we can stamp them directly with the
+	 * cycle and zero the rest of the cycle data in the extended headers.
+	 */
+	if (hdrs > 1) {
+		for (i = 1; i < hdrs; i++) {
+			p = nextfunc(p, BBSIZE, private);
+			memset(p, 0, BBSIZE);
+			/* xlog_rec_ext_header.xh_cycle */
+			*(__be32 *)p = cycle_lsn;
+		}
+	}
+
+	/*
+	 * The total length is the max of the stripe unit or 2 basic block
+	 * minimum (1 hdr blk + 1 data blk). The record length is the total
+	 * minus however many header blocks are required.
+	 */
+	head->h_len = cpu_to_be32(MAX(BBTOB(2), sunit) - hdrs * BBSIZE);
+
+	/*
+	 * Write out the unmount record, pack the first word into the record
+	 * header and stamp the block with the cycle.
+	 */
+	p = nextfunc(p, BBSIZE, private);
+	unmount_record(p);
+
 	head->h_cycle_data[0] = *(__be32 *)p;
 	*(__be32 *)p = cycle_lsn;
 
 	/*
-	 * Now zero any remaining blocks in the record and stamp with the cycle.
-	 * Note that we don't need to swap into h_cycle_data because it has
-	 * already been initialized to zero.
+	 * Finally, zero all remaining blocks in the record and stamp each with
+	 * the cycle. We don't need to pack any of these blocks because the
+	 * cycle data in the headers has already been zeroed.
 	 */
-	len = MAX(len, 2);
-	for (i = 2; i < len; i++) {
+	len = MAX(len, hdrs + 1);
+	for (i = hdrs + 1; i < len; i++) {
 		p = nextfunc(p, BBSIZE, private);
 		memset(p, 0, BBSIZE);
 		*(__be32 *)p = cycle_lsn;
Signature · 6 lines
2.1.0

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#3 lore raw

PATCH 3/4 libxfs: conditionalize log format record size optimization

In reply to Brian Foster

The libxfs log clear mechanism includes an optimization to use the maximum log buffer size (256k) when the log is being fully written (i.e., cycle != 1). This behavior is always enabled as no current callers are concerned with the size of the records written to format the log to a given cycle.

A log format command will be added to xfs_db to facilitate testing of the userspace log format code (among other things). This command is not performance oriented and prefers the ability to format the log with varying record sizes.

Update libxfs_log_clear() to use a new parameter to enable or disable the record size optimization. Note that the flag is a no-op when the cycle == XLOG_INIT_CYCLE (e.g., mkfs).

---
 copy/xfs_copy.c     |  2 +-
 db/metadump.c       |  2 +-
 db/sb.c             |  2 +-
 include/libxfs.h    |  2 +-
 libxfs/rdwr.c       | 15 ++++++++++++---
 mkfs/xfs_mkfs.c     |  3 ++-
 repair/phase2.c     |  2 +-
 repair/xfs_repair.c |  2 +-
 8 files changed, 20 insertions(+), 10 deletions(-)
copy/xfs_copy.c +1 -1
index 1dc7c65..62edfc8 100644
@@ -1328,7 +1328,7 @@ format_log(
 	 */
 	libxfs_log_clear(NULL, buf->data, logstart, length, &buf->owner->uuid,
 			 xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1,
-			 mp->m_sb.sb_logsunit, XLOG_FMT, cycle);
+			 mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true);
 	if (do_write(buf->owner, buf))
 		target[tcarg->id].state = INACTIVE;
 }
db/metadump.c +1 -1
index 8cdcb92..8455fdd 100644
@@ -2552,7 +2552,7 @@ copy_log(void)
 
 		libxfs_log_clear(NULL, iocur_top->data, logstart, logblocks,
 				 &mp->m_sb.sb_uuid, logversion,
-				 mp->m_sb.sb_logsunit, XLOG_FMT, cycle);
+				 mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true);
 		break;
 	case 1:
 		/* keep the dirty log */
db/sb.c +1 -1
index 17d446c..ee3927d 100644
@@ -288,7 +288,7 @@ sb_logzero(uuid_t *uuidp)
 			(xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks),
 			uuidp,
 			xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1,
-			mp->m_sb.sb_logsunit, XLOG_FMT, cycle);
+			mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true);
 	if (error) {
 		dbprintf(_("ERROR: cannot clear the log\n"));
 		return 0;
include/libxfs.h +1 -1
index 04c6f9c..bd51df0 100644
@@ -154,7 +154,7 @@ typedef char	*(libxfs_get_block_t)(char *, int, void *);
  */
 #define XLOG_INIT_CYCLE	1
 extern int	libxfs_log_clear(struct xfs_buftarg *, char *, xfs_daddr_t,
-				 uint, uuid_t *, int, int, int, int);
+				 uint, uuid_t *, int, int, int, int, bool);
 extern int	libxfs_log_header(char *, uuid_t *, int, int, int, xfs_lsn_t,
 				  xfs_lsn_t, libxfs_get_block_t *, void *);
 
libxfs/rdwr.c +12 -3
index 16904d0..7a04985 100644
@@ -162,7 +162,8 @@ libxfs_log_clear(
 	int			version,
 	int			sunit,		/* bytes */
 	int			fmt,
-	int			cycle)
+	int			cycle,
+	bool			max)
 {
 	struct xfs_buf		*bp = NULL;
 	int			len;
@@ -218,6 +219,14 @@ libxfs_log_clear(
 		return 0;
 
 	/*
+	 * Bump the record size for a full log format if the caller allows it.
+	 * This is primarily for performance reasons and most callers don't care
+	 * about record size since the log is clean after we're done.
+	 */
+	if (max)
+		len = BTOBB(BDSTRAT_SIZE);
+
+	/*
 	 * Otherwise, fill everything beyond the initial record with records of
 	 * the previous cycle so the kernel head/tail detection works correctly.
 	 *
@@ -233,7 +242,7 @@ libxfs_log_clear(
 		dptr += BBTOB(len);
 	end_blk = start + length;
 
-	len = min(end_blk - blk, BTOBB(BDSTRAT_SIZE));
+	len = min(end_blk - blk, len);
 	while (blk < end_blk) {
 		lsn = xlog_assign_lsn(cycle, blk - start);
 		tail_lsn = xlog_assign_lsn(cycle, blk - start - len);
@@ -257,7 +266,7 @@ libxfs_log_clear(
 		blk += len;
 		if (dptr)
 			dptr += BBTOB(len);
-		len = min(end_blk - blk, BTOBB(BDSTRAT_SIZE));
+		len = min(end_blk - blk, len);
 	}
 
 	return 0;
mkfs/xfs_mkfs.c +2 -1
index 7cba41a..546108d 100644
@@ -2713,7 +2713,8 @@ _("size %s specified for log subvolume is too large, maximum is %lld blocks\n"),
 	libxfs_log_clear(mp->m_logdev_targp, NULL,
 		XFS_FSB_TO_DADDR(mp, logstart),
 		(xfs_extlen_t)XFS_FSB_TO_BB(mp, logblocks),
-		&sbp->sb_uuid, logversion, lsunit, XLOG_FMT, XLOG_INIT_CYCLE);
+		&sbp->sb_uuid, logversion, lsunit, XLOG_FMT, XLOG_INIT_CYCLE,
+		false);
 
 	mp = libxfs_mount(mp, sbp, xi.ddev, xi.logdev, xi.rtdev, 0);
 	if (mp == NULL) {
repair/phase2.c +1 -1
index cb24711..e21ffa6 100644
@@ -119,7 +119,7 @@ zero_log(
 			(xfs_extlen_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks),
 			&mp->m_sb.sb_uuid,
 			xfs_sb_version_haslogv2(&mp->m_sb) ? 2 : 1,
-			mp->m_sb.sb_logsunit, XLOG_FMT, XLOG_INIT_CYCLE);
+			mp->m_sb.sb_logsunit, XLOG_FMT, XLOG_INIT_CYCLE, true);
 
 		/* update the log data structure with new state */
 		error = xlog_find_tail(log, &head_blk, &tail_blk);
repair/xfs_repair.c +1 -1
index 1aeac5b..3eaced4 100644
@@ -586,7 +586,7 @@ format_log_max_lsn(
 	do_warn(_("Format log to cycle %d.\n"), new_cycle);
 	libxfs_log_clear(log->l_dev, NULL, logstart, logblocks,
 			 &mp->m_sb.sb_uuid, logversion, mp->m_sb.sb_logsunit,
-			 XLOG_FMT, new_cycle);
+			 XLOG_FMT, new_cycle, true);
 }
 
 int
Signature · 6 lines
2.1.0

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs
#4 lore raw

PATCH 4/4 db: add the logformat command

In reply to Brian Foster

Add the new logformat command to format the log to a specified log cycle and/or log stripe unit. This command is primarily for testing purposes and is only available in expert mode.

---
 db/Makefile    |   4 +-
 db/command.c   |   2 +
 db/logformat.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 db/logformat.h |  19 ++++++++
 4 files changed, 172 insertions(+), 2 deletions(-)
 create mode 100644 db/logformat.c
 create mode 100644 db/logformat.h
db/Makefile +2 -2
index fb01bdd..8260da3 100644
@@ -11,8 +11,8 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
 	btblock.h bmroot.h check.h command.h convert.h debug.h \
 	dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
 	flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
-	io.h malloc.h metadump.h output.h print.h quit.h sb.h sig.h strvec.h \
-	text.h type.h write.h attrset.h symlink.h
+	io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
+	 sig.h strvec.h text.h type.h write.h attrset.h symlink.h
 CFILES = $(HFILES:.h=.c)
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
db/command.c +2 -0
index 2189e00..3c17a1e 100644
@@ -40,6 +40,7 @@
 #include "inode.h"
 #include "input.h"
 #include "io.h"
+#include "logformat.h"
 #include "metadump.h"
 #include "output.h"
 #include "print.h"
@@ -131,6 +132,7 @@ init_commands(void)
 	hash_init();
 	inode_init();
 	input_init();
+	logformat_init();
 	io_init();
 	metadump_init();
 	output_init();
db/logformat.c new file +149 -0

149 lines not on this page · raw message

db/logformat.h new file +19 -0
new file mode 100644
index 0000000..f9763ee
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2015 Red Hat, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+void logformat_init(void);
Signature · 6 lines
2.1.0

_______________________________________________
xfs mailing list
[email protected]
http://oss.sgi.com/mailman/listinfo/xfs