xfsprogs: log format fixes, db command
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
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
@@ -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
PATCH 2/4 libxfs: format the log with valid log record headers
In reply to Brian Fosterxfsprogs 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
@@ -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
PATCH 3/4 libxfs: conditionalize log format record size optimization
In reply to Brian FosterThe 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
@@ -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
@@ -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
@@ -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
@@ -154,7 +154,7 @@ typedef char *(libxfs_get_block_t)(char *, int, void *);*/ #define XLOG_INIT_CYCLE 1extern 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH 4/4 db: add the logformat command
In reply to Brian FosterAdd 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
@@ -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
@@ -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
@@ -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