From: Jian Yu Date: Fri, 15 Sep 2017 21:43:10 +0000 (+0000) Subject: LU-9771 flr: lfs mirror create and extend commands X-Git-Tag: 2.10.56~9^2^2~8 X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=commitdiff_plain;h=125f98fb5c103a164a2f81615204798679f94fd7 LU-9771 flr: lfs mirror create and extend commands This patch adds "lfs mirror create" and "lfs mirror extend" commands to create or extend a mirrored file. Usage: lfs mirror create <--mirror-count|-N[mirror_count]> [setstripe options|--parent] ... lfs mirror extend [--no-verify] <--mirror-count|-N[mirror_count]> [setstripe options|--parent|-f ] ... Options: --mirror-count|-N[mirror_count] Number of mirrors to be created with the upcoming setstripe layout options. The option can be repeated multiple times to separate mirrors that have different layouts. The mirror_count argument is optional and defaults to 1 if it's not specified; if specified, it must follow the option without a space. setstripe options The layout of one mirror. The options are the same as those for lfs setstripe command. If setstripe options are not specified, then the stripe options inherited from the previous component will be used. --parent This option indicates that the default stripe options inherited from parent directory will be used. -f The layout of victim_file will be split and used as a mirror added to the mirrored file. --no-verify This option indicates not to verify the mirror(s) from victim file(s) in case the victim file(s) contains the same data as the original mirrored file. Test-Parameters: testlist=sanity-flr Signed-off-by: Jian Yu Signed-off-by: Jinshan Xiong Change-Id: I8fb4a367bd169073f194ddee5c26d65b557e8201 Reviewed-on: https://review.whamcloud.com/29100 Tested-by: Jenkins Reviewed-by: Bobi Jam Reviewed-by: Andreas Dilger Reviewed-by: Dmitry Eremin Tested-by: Maloo --- diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index 4089c5c..8d63f45 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -44,6 +44,11 @@ MANFILES = \ lfs-ladvise.1 \ lfs_migrate.1 \ lfs-migrate.1 \ + lfs-mirror-create.1 \ + lfs-mirror-extend.1 \ + lfs-mirror-resync.1 \ + lfs-mirror-split.1 \ + lfs-mirror-verify.1 \ lfs-mkdir.1 \ lfs-setdirstripe.1 \ lfs-setstripe.1 \ diff --git a/lustre/doc/lfs-mirror-create.1 b/lustre/doc/lfs-mirror-create.1 new file mode 100644 index 0000000..31310af --- /dev/null +++ b/lustre/doc/lfs-mirror-create.1 @@ -0,0 +1,85 @@ +.TH LFS-MIRROR-CREATE 1 2017-07-25 "Lustre" "Lustre Utilities" +.SH NAME +lfs mirror create \- create a mirrored file or directory +.SH SYNOPSIS +.B lfs mirror create +<\fB\-\-mirror\-count\fR|\fB\-N\fR[\fImirror_count\fR]> +.RI [ setstripe_options | \fB--parent ] ... +.RI < filename | directory > +.SH DESCRIPTION +This command creates a mirrored file or directory specified by the path name +\fIfilename\fR or \fIdirectory\fR. +.br +The \fB\-\-mirror\-count\fR|\fB\-N\fR option is required and indicates how many +mirrors that have the same layout will be created. It can be repeated multiple +times to separate mirrors that have different layouts. The \fImirror_count\fR +argument is optional and defaults to 1 if it's not specified; if specified, it +must follow the option without a space. +.br +The \fIsetstripe_options\fR specify the specific layout for the mirror. It can +be a plain layout with specific striping pattern or a composite layout like +Progressive File Layout (PFL) (see \fBlfs-setstripe\fR(1)). +If \fIsetstripe_options\fR are not specified, +then the stripe options inherited from the previous component will be used. If +there is no previous component or \fB\-\-parent\fR option is specified, then the +default stripe options inherited from parent directory will be used. For stripe +options, only \fIstripe_count\fR, \fIstripe_size\fR and OST \fIpool_name\fR can +be inherited. +.br +If no option is specified, then the command will return an error. +.SH OPTIONS +.TP +.BR \-\-mirror\-count\fR|\fB\-N\fR[\fImirror_count\fR] +The number of mirrors that have the same layout to be created. The option can be +repeated multiple times to separate mirrors that have different layouts. The +\fImirror_count\fR argument is optional and defaults to 1 if it's not specified; +if specified, it must follow the option without a space. +.TP +.I setstripe_options +The layout of one mirror. The options are the same as those for +\fBlfs-setstripe\fR(1) command. +If \fIsetstripe_options\fR are not specified, then +the stripe options inherited from the previous component will be used. +.TP +.B \-\-parent +This option indicates that the default stripe options inherited from parent +directory will be used. +.SH EXAMPLES +.TP +.B lfs mirror create -N2 /mnt/lustre/file1 +Create a mirrored file with 2 mirrors. Each mirror has the same default striping +pattern inherited from parent directory or filesystem-wide default. +.TP +.B lfs mirror create -N2 -E 1M -E eof -c -1 /mnt/lustre/dir1 +Create a mirrored directory with 2 PFL mirrors. Each mirror has the same +specified PFL layout. +.LP +.B lfs mirror create -N3 -E 1M -c 1 -E 32M -c 4 -S 16M -E eof -c -1 +.B /mnt/lustre/file1 +.in +Create a mirrored file with 3 PFL mirrors. Each mirror has the same specified +PFL layout. +.TP +.B lfs mirror create -N -c 1 -S 4M -N -c 2 -o 2,3 -N --parent /mnt/lustre/file1 +Create a mirrored file with 3 plain layout mirrors. The first mirror has a +single stripe and 4MB stripe size. The second mirror has two stripes and locates +on OSTs with indices 2 and 3. It also has 4MB stripe size inherited from the +first mirror. The third mirror has default striping pattern inherited from +parent directory. +.LP +.B lfs mirror create -N2 -E 4M -c 2 --pool flash -E eof -c 4 -N3 -E 16M -c 4 -S +.B 16M --pool archive -E eof -c -1 /mnt/lustre/file1 +.in +Create a mirrored file with 5 PFL mirrors. The first and second mirrors have the +same PFL layout, and both of the components are allocated from the \fBflash\fR +OST pool. The last three mirrors have the same PFL layout, and each of these +components have a stripe size of 16MB and use OSTs in the \fBarchive\fR pool. +.SH AUTHOR +The \fBlfs mirror create\fR command is part of the Lustre filesystem. +.SH SEE ALSO +.BR lfs (1), +.BR lfs-setstripe (1), +.BR lfs-mirror-extend (1), +.BR lfs-mirror-split (1), +.BR lfs-mirror-resync (1), +.BR lfs-mirror-verify (1) diff --git a/lustre/doc/lfs-mirror-extend.1 b/lustre/doc/lfs-mirror-extend.1 new file mode 100644 index 0000000..22c06a5 --- /dev/null +++ b/lustre/doc/lfs-mirror-extend.1 @@ -0,0 +1,116 @@ +.TH LFS-MIRROR-EXTEND 1 2017-07-25 "Lustre" "Lustre Utilities" +.SH NAME +lfs mirror extend \- add mirror(s) to an existing file +.SH SYNOPSIS +.B lfs mirror extend +[\fB\-\-no\-verify\fR] +<\fB\-\-mirror\-count\fR|\fB\-N\fR[\fImirror_count\fR]> +[\fIsetstripe_options\fR|\fB\-\-parent\fR|\fB\-f\fR <\fIvictim_file\fR>] ... +<\fIfilename\fR> +.SH DESCRIPTION +This command adds mirror(s) to an existing file specified by the path name +\fIfilename\fR. +.br +The file \fIfilename\fR can already be a mirrored file, or just a regular +non-mirrored file. If it's a non-mirrored file, then the command will convert it +to a mirrored file. +.br +The \fB\-\-mirror\-count\fR|\fB\-N\fR option is required and indicates how many +mirrors that have the same layout will be added. It can be repeated multiple +times to separate mirrors that have different layouts. The \fImirror_count\fR +argument is optional and defaults to 1 if it's not specified; if specified, it +must follow the option without a space. +.br +The \fIsetstripe_options\fR specify the specific layout for the mirror. It can +be a plain layout with specific striping pattern or a composite layout like +Progressive File Layout (PFL) (see \fBlfs-setstripe\fR(1)). +If \fIsetstripe_options\fR are not specified, +then the stripe options inherited from the previous component will be used. If +\fB\-\-parent\fR option is specified, then the default stripe options inherited +from parent directory will be used. For stripe options, only \fIstripe_count\fR, +\fIstripe_size\fR and OST \fIpool_name\fR can be inherited. +If \fIvictim_file\fR exists, then the +command will split the layout from that file and use it as a mirror added to the +mirrored file. After the command is finished, the victim file will be removed. +The \fIsetstripe_options\fR and \fB\-\-parent\fR option cannot be specified with +\fB\-f\fR <\fIvictim_file\fR> option in one command line. +.br +If \fIvictim_file\R is specified, the utility will verify that the file contents +from \fIvictim_file\fR are the same as \fIfilename\fR. Otherwise the command +will return failure. However, option \fB\-\-no\-verify\fR can be used to +override this verification. The option can save siginificant time on file +comparison if the file size is large, but use it only when the file contents +are known to be the same. +.br +If no option is specified, then the command will return an error. +.SH OPTIONS +.TP +.BR \-\-mirror\-count\fR|\fB\-N\fR[\fImirror_count\fR] +The number of mirrors that have the same layout to be added. The option can be +repeated multiple times to separate mirrors that have different layouts. The +\fImirror_count\fR argument is optional and defaults to 1 if it's not specified; +if specified, it must follow the option without a space. +.TP +.I setstripe_options +The layout of one mirror. The options are the same as those for +\fBlfs-setstripe\fR(1) command. +If \fIsetstripe_options\fR are not specified, then the stripe options inherited +from the previous component will be used. This option cannot be specified with +\fB\-f\fR <\fIvictim_file\fR> option. +.TP +.BR \-\-parent +This option indicates that the default stripe options inherited from parent +directory will be used. +It cannot be specified with \fB\-f\fR <\fIvictim_file\fR> option. +.TP +.BR \-f\fR\ <\fIvictim_file\fR> +The layout of \fIvictim_file\fR will be split and used as a mirror added to the +mirrored file. This option cannot be specified with \fIsetstripe_options\fR or +\fB\-\-parent\fR option. +.TP +.BR \-\-no\-verify +This option indicates not to verify the mirror(s) from victim file(s) in case +the victim file(s) contains the same data as the original mirrored file. +.SH EXAMPLES +.TP +.B lfs mirror extend -N2 /mnt/lustre/file1 +Add 2 mirrors to /mnt/lustre/file1. If file1 is a non-mirrored file, then the +command will convert it to a mirrored file first and then add mirrors. Each +mirror has the same striping pattern inherited from parent directory. +.LP +.B lfs mirror extend -N3 -E 1M -c 1 -E 32M -c 4 -S 16M -E eof -c -1 +.B /mnt/lustre/file1 +.in +Add 3 PFL mirrors to /mnt/lustre/file1. Each mirror has the same specified PFL +layout. +.TP +.B lfs mirror extend -N -c 1 -S 4M -N -c 2 -o 2,3 -N --parent /mnt/lustre/file1 +Add 3 plain layout mirrors to /mnt/lustre/file1. The first mirror has a single +stripe and 4MB stripe size. The second mirror has two stripes and locates on +OSTs with indices 2 and 3. It also has 4MB stripe size inherited from the first +mirror. The third mirror has default striping pattern inherited from parent +directory. +.LP +.B lfs mirror extend -N2 -E 4M -c 2 --pool flash -E eof -c 4 -N3 -E 16M -c 4 +.B -S 16M --pool archive -E eof -c -1 /mnt/lustre/file1 +.in +Add 5 PFL mirrors to /mnt/lustre/file1. The first and second mirrors have the +same PFL layout. All of the components are allocated from the flash OST pool. +The last three mirrors have the same PFL layout. All of these components have a +stripe size of 16MB and use OSTs in the archive pool. +.LP +.B lfs mirror extend --no-verify -N -f /mnt/lustre/file2 -N -f /mnt/lustre/file3 +.B /mnt/lustre/file1 +.in +Split the layouts from /mnt/lustre/file2 and /mnt/lustre/file3, which contain +the same data as /mnt/lustre/file1, use the layouts as mirrors and add them to +/mnt/lustre/file1 without verification. +.SH AUTHOR +The \fBlfs mirror extend\fR command is part of the Lustre filesystem. +.SH SEE ALSO +.BR lfs (1), +.BR lfs-setstripe (1), +.BR lfs-mirror-create (1), +.BR lfs-mirror-split (1), +.BR lfs-mirror-resync (1), +.BR lfs-mirror-verify (1) diff --git a/lustre/doc/lfs-mirror-resync.1 b/lustre/doc/lfs-mirror-resync.1 new file mode 100644 index 0000000..11eea0f --- /dev/null +++ b/lustre/doc/lfs-mirror-resync.1 @@ -0,0 +1,39 @@ +.TH LFS-MIRROR-RESYNC 1 2017-07-25 "Lustre" "Lustre Utilities" +.SH NAME +lfs mirror resync \- resynchronize an out-of-sync mirrored file +.SH SYNOPSIS +.B lfs mirror resync +[\fB\-\-only\fR <\fImirror_id\fR[,...]>] +<\fImirrored_file\fR> +.SH DESCRIPTION +This command resynchronizes an out-of-sync mirrored file specified by the path +name \fImirrored_file\fR. +.br +If there is no stale mirror for the \fImirrored_file\fR, then the command does +nothing. Otherwise, it will copy data from synced mirror to stale mirror(s), and +mark all successfully copied mirror(s) as SYNC. +If \fB\-\-only\fR <\fImirror_id\fR[,...]> option is specified, then the +command will resynchronize the mirror(s) specified by the \fImirror_id\fR(s). +.SH OPTIONS +.TP +.BR \-\-only\fR\ <\fImirror_id\fR[,...]> +This option indicates which mirror(s) specified by \fImirror_id\fR(s) needs to +be resynchronized. The \fImirror_id\fR is the numerical unique identifier for +a mirror. Multiple \fImirror_id\fRs are separated by comma. +.SH EXAMPLES +.TP +.B lfs mirror resync /mnt/lustre/file1 +Resynchronize all of the stale mirror(s) for /mnt/lustre/file1. +.TP +.B lfs mirror resync --only 4,5 /mnt/lustre/file1 +Resynchronize mirrors with mirror ID 4 and 5 for /mnt/lustre/file1 even if they +are not marked as STALE. +.SH AUTHOR +The \fBlfs mirror resync\fR command is part of the Lustre filesystem. +.SH SEE ALSO +.BR lfs (1), +.BR lfs-setstripe (1), +.BR lfs-mirror-create (1), +.BR lfs-mirror-extend (1), +.BR lfs-mirror-split (1), +.BR lfs-mirror-verify (1) diff --git a/lustre/doc/lfs-mirror-split.1 b/lustre/doc/lfs-mirror-split.1 new file mode 100644 index 0000000..b68336a --- /dev/null +++ b/lustre/doc/lfs-mirror-split.1 @@ -0,0 +1,57 @@ +.TH LFS-MIRROR-SPLIT 1 2017-07-25 "Lustre" "Lustre Utilities" +.SH NAME +lfs mirror split \- split a specified mirror from an existing mirrored file +.SH SYNOPSIS +.B lfs mirror split +<\fB\-\-mirror\-id\fR <\fImirror_id\fR>> +[\fB\-\-destroy\fR|\fB\-d\fR] +[\fB\-f\fR <\fInew_file\fR>] +<\fImirrored_file\fR> +.SH DESCRIPTION +This command splits a mirror with ID <\fImirror_id\fR> out of a mirrored +file specified by the path name \fImirrored_file\fR. By default, the layout of +the split mirror will be stored into a new file named +<\fImirrored_file\fR>.mirror~<\fImirror_id\fR>. If \fB\-\-destroy\fR|\fB\-d\fR +option is specified, then the split mirror will be destroyed. +If \fB\-f\fR <\fInew_file\fR> option is specified, then the layout of the split +mirror will be stored into the named file. +.br +If \fImirrored_file\fR has only one mirror existing after split, it will be +converted to a regular non-mirrored file. +.br +If the original \fImirrored_file\fR is not a mirrored file, then the command +will return an error. +.SH OPTIONS +.TP +.BR \-\-mirror\-id\fR\ <\fImirror_id\fR> +The numerical unique identifier for a mirror. The mirror ID is unique within a +mirrored file and is automatically assigned at file creation or extension time. +It can be fetched by \fBlfs getstripe\fR command (see \fBlfs(1)\fR). +.TP +.BR \-\-destroy\fR|\fB\-d\fR +This option indicates the split mirror will be destroyed. +.TP +.BR \-f\fR\ <\fInew_file\fR> +This option indicates the layout of the split mirror will be stored into +<\fInew_file\fR>. +.SH EXAMPLES +.TP +.B lfs mirror split --mirror-id 1 /mnt/lustre/file1 +Split a mirror with ID 1 out of /mnt/lustre/file1 and store it into +/mnt/lustre/file1.mirror~1. +.TP +.B lfs mirror split --mirror-id 2 -d /mnt/lustre/file1 +Split a mirror with ID 2 out of /mnt/lustre/file1 and destroy it. +.TP +.B lfs mirror split --mirror-id 3 -f /mnt/lustre/file2 /mnt/lustre/file1 +Split a mirror with ID 3 out of /mnt/lustre/file1 and store it into +/mnt/lustre/file2. +.SH AUTHOR +The \fBlfs mirror split\fR command is part of the Lustre filesystem. +.SH SEE ALSO +.BR lfs (1), +.BR lfs-setstripe (1), +.BR lfs-mirror-create (1), +.BR lfs-mirror-extend (1), +.BR lfs-mirror-resync (1), +.BR lfs-mirror-verify (1) diff --git a/lustre/doc/lfs-mirror-verify.1 b/lustre/doc/lfs-mirror-verify.1 new file mode 100644 index 0000000..98a7f47 --- /dev/null +++ b/lustre/doc/lfs-mirror-verify.1 @@ -0,0 +1,43 @@ +.TH LFS-MIRROR-VERIFY 1 2017-07-25 "Lustre" "Lustre Utilities" +.SH NAME +lfs mirror verify \- verify a mirrored file +.SH SYNOPSIS +.B lfs mirror verify +[\fB\-\-only\fR <\fImirror_id\fR[,...]>] +<\fImirrored_file\fR> +.SH DESCRIPTION +This command verifies that each SYNC mirror of a mirrored file specified by the +path name \fImirrored_file\fR contains exactly the same data. +.br +This is a scrub tool that should be run in regular basis to make sure that +mirrored files are not corrupted. The command won't repair the file if it turns +out to be corrupted. Usually administrator should check the file content from +each mirror and decide which one is correct and then invoke \fBlfs\ mirror +\ resync\fR to repair it manually. +.br +If \fB\-\-only\fR <\fImirror_id\fR[,...]> option is specified, then the +command will verify the mirror(s) specified by \fImirror_id\fR(s) contains +exactly the same data as the other mirrors for the mirrored file. +.SH OPTIONS +.TP +.BR \-\-only\fR\ <\fImirror_id\fR[,...]> +This option indicates which mirror(s) specified by \fImirror_id\fR(s) needs to +be verified. The \fImirror_id\fR is the numerical unique identifier for +a mirror. Multiple \fImirror_id\fRs are separated by comma. +.SH EXAMPLES +.TP +.B lfs mirror verify /mnt/lustre/file1 +Verify that each mirror of /mnt/lustre/file1 contains exactly the same data. +.TP +.B lfs mirror verify --only 4,5 /mnt/lustre/file1 +Verify mirrors with mirror ID 4 and 5 contain exactly the same data as other +mirrors for /mnt/lustre/file1. +.SH AUTHOR +The \fBlfs mirror verify\fR command is part of the Lustre filesystem. +.SH SEE ALSO +.BR lfs (1), +.BR lfs-setstripe (1), +.BR lfs-mirror-create (1), +.BR lfs-mirror-extend (1), +.BR lfs-mirror-split (1), +.BR lfs-mirror-resync (1) diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index 5636ad0..5fc9f86 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -502,6 +502,19 @@ struct llapi_layout *llapi_layout_alloc(void); */ void llapi_layout_free(struct llapi_layout *layout); +/** + * llapi_layout_merge() - Merge a composite layout into another one. + * @dst_layout: Destination composite layout. + * @src_layout: Source composite layout. + * + * This function copies all of the components from @src_layout and + * appends them to @dst_layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_merge(struct llapi_layout **dst_layout, + const struct llapi_layout *src_layout); + /** Not a valid stripe size, offset, or RAID pattern. */ #define LLAPI_LAYOUT_INVALID 0x1000000000000001ULL @@ -731,6 +744,31 @@ int llapi_layout_flags_set(struct llapi_layout *layout, uint32_t flags); int llapi_layout_flags_get(struct llapi_layout *layout, uint32_t *flags); /** + * llapi_layout_mirror_count_get() - Get mirror count from the header of + * a layout. + * @layout: Layout to get mirror count from. + * @count: Returned mirror count value. + * + * This function gets mirror count from the header of a layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_mirror_count_get(struct llapi_layout *layout, + uint16_t *count); + +/** + * llapi_layout_mirror_count_set() - Set mirror count to the header of a layout. + * @layout: Layout to set mirror count in. + * @count: Mirror count value to be set. + * + * This function sets mirror count to the header of a layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_mirror_count_set(struct llapi_layout *layout, + uint16_t count); + +/** * Fetch the start and end offset of the current layout component. */ int llapi_layout_comp_extent_get(const struct llapi_layout *layout, diff --git a/lustre/tests/sanity-flr.sh b/lustre/tests/sanity-flr.sh index 3e50c77..a88b3b0 100644 --- a/lustre/tests/sanity-flr.sh +++ b/lustre/tests/sanity-flr.sh @@ -2,8 +2,6 @@ # # Run select tests by setting ONLY, or as arguments to the script. # Skip specific tests by setting EXCEPT. -# -# Run test by setting NOSETUP=true when ltest has setup env for us set -e set +o posix @@ -15,35 +13,26 @@ ONLY=${ONLY:-"$*"} ALWAYS_EXCEPT="$SANITY_FLR_EXCEPT 201" # UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT! -[ "$ALWAYS_EXCEPT$EXCEPT" ] && - echo "Skipping tests: $ALWAYS_EXCEPT $EXCEPT" - -TMP=${TMP:-/tmp} -CHECKSTAT=${CHECKSTAT:-"checkstat -v"} -LFS=${LFS:-lfs} -LCTL=${LCTL:-lctl} -MULTIOP=${MULTIOP:-multiop} - LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)} . $LUSTRE/tests/test-framework.sh init_test_env $@ . ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh} init_logging -check_and_setup_lustre -DIR=${DIR:-$MOUNT} -assert_DIR - -if [[ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.7.64) ]]; then - skip_env "Need MDS version at least 2.7.64" && exit +if [[ $(lustre_version_code $SINGLEMDS) -lt $(version_code 2.10.53) ]]; then + skip_env "Need MDS version at least 2.10.53" && exit fi -build_test_filter - [ $UID -eq 0 -a $RUNAS_ID -eq 0 ] && error "\$RUNAS_ID set to 0, but \$UID is also 0!" check_runas_id $RUNAS_ID $RUNAS_GID $RUNAS +check_and_setup_lustre +DIR=${DIR:-$MOUNT} +assert_DIR + +build_test_filter + # global array to store mirror IDs declare -a mirror_array get_mirror_ids() { @@ -94,24 +83,25 @@ start_osts() { test_1() { local tf=$DIR/$tfile local mirror_count=16 # LUSTRE_MIRROR_COUNT_MAX - - $LFS setstripe -E EOF -c -1 $tf - + local mirror_create_cmd="$LFS mirror create" local stripes[0]=$OSTCOUNT + mirror_create_cmd+=" -N -c ${stripes[0]}" for ((i = 1; i < $mirror_count; i++)); do # add mirrors with different stripes to the file stripes[$i]=$((RANDOM % OSTCOUNT)) [ ${stripes[$i]} -eq 0 ] && stripes[$i]=1 - $LFS setstripe --component-add --mirror -c ${stripes[$i]} $tf + mirror_create_cmd+=" -N -c ${stripes[$i]}" done + eval $mirror_create_cmd $tf || error "creating mirrored file $tf failed" + [ $(get_mirror_ids $tf) -ne $mirror_count ] && error "mirror count error" # can't create mirrors exceeding LUSTRE_MIRROR_COUNT_MAX - $LFS setstripe --component-add --mirror $tf && + $LFS mirror extend -N $tf && error "Creating the $((mirror_count+1))th mirror succeeded" local ids=($($LFS getstripe $tf | awk '/lcme_id/{print $2}' | @@ -148,11 +138,11 @@ test_2() { local layout=$($LFS getstripe $tf2 | grep -A 4 lmm_objects) - $LFS setstripe --component-add --mirror=$tf2 $tf + $LFS mirror extend -N -f $tf2 $tf || + error "merging $tf2 into $tf failed" [ $(get_mirror_ids $tf) -ne 2 ] && error "mirror count should be 2" - $LFS getstripe $tf2 | grep -q 'no stripe info' || - error "$tf2 still has stripe info" + [[ ! -e $tf2 ]] || error "$tf2 was not unlinked" } run_test 2 "create components from existing files" @@ -164,7 +154,7 @@ test_3() { $LFS setstripe -E -1 $DIR/$tdir-$i/$tfile done - $LFS setstripe --component-add --mirror=$DIR/$tdir-1/$tfile \ + $LFS mirror extend -N -f $DIR/$tdir-1/$tfile \ $DIR/$tdir-0/$tfile || error "creating mirrors" # mdt doesn't support to cancel layout lock for remote objects, do @@ -198,7 +188,8 @@ test_21() { local blocks=$(du -kc $tf $tf2 | awk '/total/{print $1}') # add component - $LFS setstripe --component-add --mirror=$tf2 $tf + $LFS mirror extend -N -f $tf2 $tf || + error "merging $tf2 into $tf failed" # cancel layout lock cancel_lru_locks mdc @@ -230,7 +221,8 @@ test_22() { dd if=/dev/zero of=$tf bs=1M count=$((RANDOM % 20 + 1)) # add component, two mirrors located on the same OST ;-) - $LFS setstripe --component-add --mirror -o 0 $tf + $LFS mirror extend -N -o 0 $tf || + error "extending mirrored file $tf failed" size_blocks=$(stat --format="%b %s" $tf) @@ -252,8 +244,8 @@ run_test 22 "no glimpse to OSTs for READ_ONLY files" test_31() { local tf=$DIR/$tfile - $LFS setstripe -E EOF -o 0 $tf - $LFS setstripe --component-add --mirror -o 1 $tf + $LFS mirror create -N -o 0 -N -o 1 $tf || + error "creating mirrored file $tf failed" #define OBD_FAIL_GLIMPSE_IMMUTABLE 0x1A00 $LCTL set_param fail_loc=0x1A00 @@ -298,7 +290,8 @@ test_32() { local cksum=$(md5sum $DIR/$tfile) # create a new mirror in sync mode - $LFS setstripe --component-add --mirror -o 1 $DIR/$tfile + $LFS mirror extend -N -o 1 $DIR/$tfile || + error "extending mirrored file $DIR/$tfile failed" # make sure the mirrored file was created successfully [ $(get_mirror_ids $DIR/$tfile) -eq 2 ] || @@ -342,13 +335,18 @@ test_33() { done # create a mirrored file - $LFS setstripe --component-add --mirror=$DIR/$tfile-2 $DIR/$tfile - - # make sure that $tfile has two mirrors and $tfile-2 has no stripe + $LFS mirror extend -N -f $DIR/$tfile-2 $DIR/$tfile && + error "merging $DIR/$tfile-2 into $DIR/$tfile" \ + "with verification should fail" + $LFS mirror extend --no-verify -N -f $DIR/$tfile-2 $DIR/$tfile || + error "merging $DIR/$tfile-2 into $DIR/$tfile" \ + "without verification failed" + + # make sure that $tfile has two mirrors and $tfile-2 does not exist [ $(get_mirror_ids $DIR/$tfile) -eq 2 ] || { $LFS getstripe $DIR/$tfile; error "expected count 2"; } - $LFS getstripe $DIR/$tfile-2 | grep -q "no stripe info" || - { $LFS getstripe $DIR/$tfile; error "expected no stripe"; } + + [[ ! -e $DIR/$tfile-2 ]] || error "$DIR/$tfile-2 was not unlinked" # execpted file size local fsize=$((5 * max_count)) @@ -412,7 +410,8 @@ test_34a() { error "mirrored file size is not 3M" # merge a mirrored file - $LFS setstripe --component-add --mirror=$DIR/$tfile-2 $DIR/$tfile + $LFS mirror extend -N -f $DIR/$tfile-2 $DIR/$tfile || + error "merging $DIR/$tfile-2 into $DIR/$tfile failed" cancel_lru_locks osc @@ -450,7 +449,8 @@ test_34b() { error "mirrored file size is not 3M" # merge a mirrored file - $LFS setstripe --component-add --mirror=$DIR/$tfile-2 $DIR/$tfile + $LFS mirror extend -N -f $DIR/$tfile-2 $DIR/$tfile || + error "merging $DIR/$tfile-2 into $DIR/$tfile failed" cancel_lru_locks osc @@ -475,7 +475,8 @@ test_35() { $LFS setstripe -E eof $tf # add an out-of-sync mirror to the file - $LFS setstripe --component-add --mirror -c 2 $tf + $LFS mirror extend -N -c 2 $tf || + error "extending mirrored file $tf failed" $MULTIOP $tf oO_WRONLY:c || error "write open a mirrored file failed" @@ -504,8 +505,8 @@ create_file_36() { $LFS setstripe -E 1M -E 2M -E 4M -E eof -c -1 $tf $LFS setstripe -E 3M -E 6M -E eof -c -1 $tf-tmp - $LFS setstripe --component-add --mirror=$tf-tmp $tf - rm -f $tf-tmp + $LFS mirror extend -N -f $tf-tmp $tf || + error "merging $tf-tmp into $tf failed" done } @@ -596,8 +597,10 @@ test_37() printf '%s\n' "${checksums[@]}" # merge these files into a mirrored file - $LFS setstripe --component-add --mirror=$tf2 $tf - $LFS setstripe --component-add --mirror=$tf3 $tf + $LFS mirror extend --no-verify -N -f $tf2 $tf || + error "merging $tf2 into $tf failed" + $LFS mirror extend --no-verify -N -f $tf3 $tf || + error "merging $tf3 into $tf failed" get_mirror_ids $tf @@ -634,7 +637,7 @@ test_37() error "$i: mismatch checksum after copy" done - rm -f $tf $tf2 $tf3 + rm -f $tf } run_test 37 "mirror I/O API verification" @@ -660,9 +663,12 @@ test_38() { $LFS setstripe -E 4M -c 1 -E 8M -c 2 -E eof -c -1 $tf-3 # instantiate all components - $LFS setstripe --component-add --mirror=$tf-2 $tf - $LFS setstripe --component-add --mirror=$tf-3 $tf - $LFS setstripe --component-add --mirror -c 1 $tf + $LFS mirror extend -N -f $tf-2 $tf || + error "merging $tf-2 into $tf failed" + $LFS mirror extend -N -f $tf-3 $tf || + error "merging $tf-3 into $tf failed" + $LFS mirror extend -N -c 1 $tf || + error "extending mirrored file $tf failed" verify_flr_state $tf "read_only" @@ -797,8 +803,10 @@ test_200() { $LFS setstripe -E 2M -E 6M -c 2 -E 8M -E 32M -E eof $tf-2 $LFS setstripe -E 4M -c 2 -E 8M -E 64M -E eof $tf-3 - $LFS setstripe --component-add --mirror=$tf-2 $tf - $LFS setstripe --component-add --mirror=$tf-3 $tf + $LFS mirror extend -N -f $tf-2 $tf || + error "merging $tf-2 into $tf failed" + $LFS mirror extend -N -f $tf-3 $tf || + error "merging $tf-3 into $tf failed" mkdir -p $MOUNT2 && mount_client $MOUNT2 diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 6bc3f5d..a5fe967 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -250,6 +250,7 @@ init_test_env() { export SGPDDSURVEY=${SGPDDSURVEY:-"$LUSTRE/../lustre-iokit/sgpdd-survey/sgpdd-survey")} [ ! -f "$SGPDDSURVEY" ] && export SGPDDSURVEY=$(which sgpdd-survey) export MCREATE=${MCREATE:-mcreate} + export MULTIOP=${MULTIOP:-multiop} # Ubuntu, at least, has a truncate command in /usr/bin # so fully path our truncate command. export TRUNCATE=${TRUNCATE:-$LUSTRE/tests/truncate} diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 32c403b..5aebbae 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -73,7 +73,6 @@ #endif /* !ARRAY_SIZE */ /* all functions */ -static int lfs_setstripe(int argc, char **argv); static int lfs_find(int argc, char **argv); static int lfs_getstripe(int argc, char **argv); static int lfs_getdirstripe(int argc, char **argv); @@ -109,8 +108,35 @@ static int lfs_hsm_cancel(int argc, char **argv); static int lfs_swap_layouts(int argc, char **argv); static int lfs_mv(int argc, char **argv); static int lfs_ladvise(int argc, char **argv); +static int lfs_mirror(int argc, char **argv); +static int lfs_mirror_list_commands(int argc, char **argv); static int lfs_list_commands(int argc, char **argv); +enum setstripe_origin { + SO_SETSTRIPE, + SO_MIGRATE, + SO_MIRROR_CREATE, + SO_MIRROR_EXTEND +}; +static int lfs_setstripe0(int argc, char **argv, enum setstripe_origin opc); + +static inline int lfs_setstripe(int argc, char **argv) +{ + return lfs_setstripe0(argc, argv, SO_SETSTRIPE); +} +static inline int lfs_setstripe_migrate(int argc, char **argv) +{ + return lfs_setstripe0(argc, argv, SO_MIGRATE); +} +static inline int lfs_mirror_create(int argc, char **argv) +{ + return lfs_setstripe0(argc, argv, SO_MIRROR_CREATE); +} +static inline int lfs_mirror_extend(int argc, char **argv) +{ + return lfs_setstripe0(argc, argv, SO_MIRROR_EXTEND); +} + /* Setstripe and migrate share mostly the same parameters */ #define SSM_CMD_COMMON(cmd) \ "usage: "cmd" [--component-end|-E ]\n" \ @@ -141,6 +167,39 @@ static int lfs_list_commands(int argc, char **argv); "\t respectively, -1 for EOF). Must be a multiple of\n"\ "\t stripe_size.\n" +#define MIRROR_CREATE_HELP \ + "\tmirror_count: Number of mirrors to be created with the upcoming\n" \ + "\t setstripe layout options\n" \ + "\t It defaults to 1 if not specified; if specified,\n" \ + "\t it must follow the option without a space.\n" \ + "\t The option can also be repeated multiple times to\n" \ + "\t separate mirrors that have different layouts.\n" \ + "\tsetstripe options: Mirror layout\n" \ + "\t It can be a plain layout or a composite layout.\n" \ + "\t If not specified, the stripe options inherited\n" \ + "\t from the previous component will be used.\n" \ + "\tparent: Use default stripe options from parent directory\n" + +#define MIRROR_EXTEND_HELP \ + MIRROR_CREATE_HELP \ + "\tvictim_file: The layout of victim_file will be split and used\n" \ + "\t as a mirror added to the mirrored file.\n" \ + "\tno-verify: This option indicates not to verify the mirror(s)\n" \ + "\t from victim file(s) in case the victim file(s)\n" \ + "\t contains the same data as the original mirrored\n" \ + "\t file.\n" + +#define MIRROR_EXTEND_USAGE \ + " <--mirror-count|-N[mirror_count]>\n" \ + " [setstripe options|--parent|-f ]\n" \ + " [--no-verify]\n" + +#define SETSTRIPE_USAGE \ + SSM_CMD_COMMON("setstripe") \ + MIRROR_EXTEND_USAGE \ + " \n" \ + SSM_HELP_COMMON \ + MIRROR_EXTEND_HELP #define MIGRATE_USAGE \ SSM_CMD_COMMON("migrate ") \ @@ -167,6 +226,30 @@ static int lfs_list_commands(int argc, char **argv); static const char *progname; +/** + * command_t mirror_cmdlist - lfs mirror commands. + */ +command_t mirror_cmdlist[] = { + { .pc_name = "create", .pc_func = lfs_mirror_create, + .pc_help = "Create a mirrored file.\n" + "usage: lfs mirror create " + "<--mirror-count|-N[mirror_count]> " + "[setstripe options|--parent] ... \n" + MIRROR_CREATE_HELP }, + { .pc_name = "extend", .pc_func = lfs_mirror_extend, + .pc_help = "Extend a mirrored file.\n" + "usage: lfs mirror extend " + "<--mirror-count|-N[mirror_count]> [--no-verify] " + "[setstripe options|--parent|-f ] ... \n" + MIRROR_EXTEND_HELP }, + { .pc_name = "--list-commands", .pc_func = lfs_mirror_list_commands, + .pc_help = "list commands supported by lfs mirror"}, + { .pc_name = "help", .pc_func = Parser_help, .pc_help = "help" }, + { .pc_name = "exit", .pc_func = Parser_quit, .pc_help = "quit" }, + { .pc_name = "quit", .pc_func = Parser_quit, .pc_help = "quit" }, + { .pc_help = NULL } +}; + /* all available commands */ command_t cmdlist[] = { {"setstripe", lfs_setstripe, 0, @@ -363,7 +446,7 @@ command_t cmdlist[] = { "usage: hsm_cancel [--filelist FILELIST] [--data DATA] ..."}, {"swap_layouts", lfs_swap_layouts, 0, "Swap layouts between 2 files.\n" "usage: swap_layouts "}, - {"migrate", lfs_setstripe, 0, + {"migrate", lfs_setstripe_migrate, 0, "migrate a directory between MDTs.\n" "usage: migrate --mdt-index [--verbose|-v] " "\n" @@ -399,6 +482,13 @@ command_t cmdlist[] = { " {[--end|-e END[kMGT]] | [--length|-l LENGTH[kMGT]]}\n" " {[--mode|-m [READ,WRITE]}\n" " ...\n"}, + {"mirror", lfs_mirror, mirror_cmdlist, + "lfs commands used to manage files with mirrored components:\n" + "lfs mirror create - create a mirrored file or directory\n" + "lfs mirror extend - add mirror(s) to an existing file\n" + "lfs mirror split - split a mirror from an existing mirrored file\n" + "lfs mirror resync - resynchronize an out-of-sync mirrored file\n" + "lfs mirror verify - verify a mirrored file\n"}, {"help", Parser_help, 0, "help"}, {"exit", Parser_quit, 0, "quit"}, {"quit", Parser_quit, 0, "quit"}, @@ -892,8 +982,209 @@ out: return rc; } -static int lfs_create_mirror(char *fname, struct llapi_layout *layout, - const char *mirror_file) +/** + * struct mirror_args - Command-line arguments for mirror(s). + * @m_count: Number of mirrors to be created with this layout. + * @m_layout: Mirror layout. + * @m_file: A victim file. Its layout will be split and used as a mirror. + * @m_next: Point to the next node of the list. + * + * Command-line arguments for mirror(s) will be parsed and stored in + * a linked list that consists of this structure. + */ +struct mirror_args { + __u32 m_count; + struct llapi_layout *m_layout; + const char *m_file; + struct mirror_args *m_next; +}; + +/** + * enum mirror_flags - Flags for extending a mirrored file. + * @NO_VERIFY: Indicates not to verify the mirror(s) from victim file(s) + * in case the victim file(s) contains the same data as the + * original mirrored file. + * + * Flags for extending a mirrored file. + */ +enum mirror_flags { + NO_VERIFY = 0x1, +}; + +/** + * mirror_create_sanity_check() - Check mirror list. + * @list: A linked list that stores the mirror arguments. + * + * This function does a sanity check on @list for creating + * a mirrored file. + * + * Return: 0 on success or a negative error code on failure. + */ +static int mirror_create_sanity_check(struct mirror_args *list) +{ + int rc = 0; + bool has_m_file = false; + bool has_m_layout = false; + + if (list == NULL) + return -EINVAL; + + while (list != NULL) { + uint64_t start, end; + + if (list->m_file != NULL) { + has_m_file = true; + llapi_layout_free(list->m_layout); + + list->m_layout = + llapi_layout_get_by_path(list->m_file, 0); + if (list->m_layout == NULL) { + fprintf(stderr, + "error: %s: file '%s' has no layout\n", + progname, list->m_file); + return -ENODATA; + } + } else { + if (list->m_layout != NULL) + has_m_layout = true; + else { + fprintf(stderr, "error: %s: no mirror layout\n", + progname); + return -EINVAL; + } + } + + rc = llapi_layout_comp_use(list->m_layout, + LLAPI_LAYOUT_COMP_USE_LAST); + if (rc) + return -errno; + + rc = llapi_layout_comp_extent_get(list->m_layout, &start, &end); + if (rc) + return -errno; + + if (end != LUSTRE_EOF) { + fprintf(stderr, + "error: %s: mirror layout doesn't reach eof\n", + progname); + return -EINVAL; + } + + list = list->m_next; + } + + if (has_m_file && has_m_layout) { + fprintf(stderr, "error: %s: -f option should not " + "be specified with setstripe options or " + "--parent option\n", progname); + return -EINVAL; + } + + return 0; +} + +/** + * mirror_create() - Create a mirrored file. + * @fname: The file to be created. + * @mirror_list: A linked list that stores the mirror arguments. + * + * This function creates a mirrored file @fname with the mirror(s) + * from @mirror_list. + * + * Return: 0 on success or a negative error code on failure. + */ +static int mirror_create(char *fname, struct mirror_args *mirror_list) +{ + struct llapi_layout *layout = NULL; + struct mirror_args *cur_mirror = NULL; + uint16_t mirror_count = 0; + int i = 0; + int rc = 0; + + rc = mirror_create_sanity_check(mirror_list); + if (rc) + return rc; + + cur_mirror = mirror_list; + while (cur_mirror != NULL) { + for (i = 0; i < cur_mirror->m_count; i++) { + rc = llapi_layout_merge(&layout, cur_mirror->m_layout); + if (rc) { + rc = -errno; + fprintf(stderr, "error: %s: " + "merge layout failed: %s\n", + progname, strerror(errno)); + goto error; + } + } + mirror_count += cur_mirror->m_count; + cur_mirror = cur_mirror->m_next; + } + + rc = llapi_layout_mirror_count_set(layout, mirror_count); + if (rc) { + rc = -errno; + fprintf(stderr, "error: %s: set mirror count failed: %s\n", + progname, strerror(errno)); + goto error; + } + + rc = lfs_component_create(fname, O_CREAT | O_WRONLY | O_EXCL, 0644, + layout); + if (rc >= 0) { + close(rc); + rc = 0; + } + +error: + llapi_layout_free(layout); + return rc; +} + +/** + * Compare files and check lease on @fd. + * + * \retval bytes number of bytes are the same + */ +static ssize_t mirror_file_compare(int fd, int fdv) +{ + const size_t buflen = 4 * 1024 * 1024; /* 4M */ + void *buf; + ssize_t bytes_done = 0; + ssize_t bytes_read = 0; + + buf = malloc(buflen * 2); + if (!buf) + return -ENOMEM; + + while (1) { + if (!llapi_lease_check(fd)) { + bytes_done = -EBUSY; + break; + } + + bytes_read = read(fd, buf, buflen); + if (bytes_read <= 0) + break; + + if (bytes_read != read(fdv, buf + buflen, buflen)) + break; + + /* XXX: should compute the checksum on each buffer and then + * compare checksum to avoid cache collision */ + if (memcmp(buf, buf + buflen, bytes_read)) + break; + + bytes_done += bytes_read; + } + + free(buf); + + return bytes_done; +} + +static int mirror_extend_file(const char *fname, const char *victim_file, + enum mirror_flags mirror_flags) { int fd = -1; int fdv = -1; @@ -902,10 +1193,6 @@ static int lfs_create_mirror(char *fname, struct llapi_layout *layout, __u64 dv; int rc; - if (mirror_file == NULL) - return lfs_migrate(fname, MIGRATION_NONBLOCK | MIGRATION_MIRROR, - NULL, layout); - fd = open(fname, O_RDWR); if (fd < 0) { error_loc = "open source file"; @@ -913,26 +1200,13 @@ static int lfs_create_mirror(char *fname, struct llapi_layout *layout, goto out; } - /* Get rid of caching pages from clients */ - rc = llapi_get_data_version(fd, &dv, LL_DV_WR_FLUSH); - if (rc < 0) { - error_loc = "cannot get data version"; - return rc; - } - - fdv = open(mirror_file, O_WRONLY); + fdv = open(victim_file, O_RDWR); if (fdv < 0) { error_loc = "open target file"; rc = -errno; goto out; } - rc = llapi_get_data_version(fdv, &dv, LL_DV_WR_FLUSH); - if (rc < 0) { - error_loc = "cannot get data version"; - return rc; - } - if (fstat(fd, &stbuf) || fstat(fdv, &stbuf_v)) { error_loc = "stat source or target file"; rc = -errno; @@ -941,7 +1215,7 @@ static int lfs_create_mirror(char *fname, struct llapi_layout *layout, if (stbuf.st_dev != stbuf_v.st_dev) { error_loc = "stat source and target file"; - rc = EXDEV; + rc = -EXDEV; goto out; } @@ -958,6 +1232,31 @@ static int lfs_create_mirror(char *fname, struct llapi_layout *layout, goto out; } + if (!(mirror_flags & NO_VERIFY)) { + ssize_t ret; + /* mirrors should have the same contents */ + ret = mirror_file_compare(fd, fdv); + if (ret != stbuf.st_size) { + error_loc = "file busy or contents don't match"; + rc = ret < 0 ? ret : -EINVAL; + goto out; + } + } + + /* Get rid of caching pages from clients */ + rc = llapi_get_data_version(fd, &dv, LL_DV_WR_FLUSH); + if (rc < 0) { + error_loc = "cannot get data version"; + return rc; + } + + rc = llapi_get_data_version(fdv, &dv, LL_DV_WR_FLUSH); + if (rc < 0) { + error_loc = "cannot get data version"; + return rc; + + } + /* Make sure we keep original atime/mtime values */ rc = migrate_copy_timestamps(fd, fdv); @@ -977,12 +1276,50 @@ out: if (fdv >= 0) close(fdv); + if (!rc) + (void) unlink(victim_file); + if (rc < 0) fprintf(stderr, "error: %s: %s: %s: %s\n", progname, fname, error_loc, strerror(-rc)); return rc; } +static int mirror_extend(char *fname, struct mirror_args *mirror_list, + enum mirror_flags mirror_flags) +{ + int rc; + + rc = mirror_create_sanity_check(mirror_list); + if (rc) + return rc; + + while (mirror_list) { + if (mirror_list->m_file != NULL) { + rc = mirror_extend_file(fname, mirror_list->m_file, + mirror_flags); + } else { + __u32 mirror_count = mirror_list->m_count; + + while (mirror_count > 0) { + rc = lfs_migrate(fname, + MIGRATION_NONBLOCK | MIGRATION_MIRROR, + NULL, mirror_list->m_layout); + if (rc) + break; + + --mirror_count; + } + } + if (rc) + break; + + mirror_list = mirror_list->m_next; + } + + return rc; +} + /** * Parse a string containing an OST index list into an array of integers. * @@ -1070,11 +1407,11 @@ static int parse_targets(__u32 *osts, int size, int offset, char *arg) struct lfs_setstripe_args { unsigned long long lsa_comp_end; unsigned long long lsa_stripe_size; - int lsa_stripe_count; - int lsa_stripe_off; + long long lsa_stripe_count; + long long lsa_stripe_off; __u32 lsa_comp_flags; int lsa_nr_osts; - int lsa_pattern; + unsigned long long lsa_pattern; __u32 *lsa_osts; char *lsa_pool_name; }; @@ -1082,16 +1419,60 @@ struct lfs_setstripe_args { static inline void setstripe_args_init(struct lfs_setstripe_args *lsa) { memset(lsa, 0, sizeof(*lsa)); - lsa->lsa_stripe_off = -1; + + lsa->lsa_stripe_size = LLAPI_LAYOUT_DEFAULT; + lsa->lsa_stripe_count = LLAPI_LAYOUT_DEFAULT; + lsa->lsa_stripe_off = LLAPI_LAYOUT_DEFAULT; + lsa->lsa_pattern = LLAPI_LAYOUT_RAID0; + lsa->lsa_pool_name = NULL; +} + +/** + * setstripe_args_init_inherit() - Initialize and inherit stripe options. + * @lsa: Stripe options to be initialized and inherited. + * + * This function initializes stripe options in @lsa and inherit + * stripe_size, stripe_count and OST pool_name options. + * + * Return: void. + */ +static inline void setstripe_args_init_inherit(struct lfs_setstripe_args *lsa) +{ + unsigned long long stripe_size; + long long stripe_count; + char *pool_name = NULL; + + stripe_size = lsa->lsa_stripe_size; + stripe_count = lsa->lsa_stripe_count; + pool_name = lsa->lsa_pool_name; + + setstripe_args_init(lsa); + + lsa->lsa_stripe_size = stripe_size; + lsa->lsa_stripe_count = stripe_count; + lsa->lsa_pool_name = pool_name; } static inline bool setstripe_args_specified(struct lfs_setstripe_args *lsa) { - return (lsa->lsa_stripe_size != 0 || lsa->lsa_stripe_count != 0 || - lsa->lsa_stripe_off != -1 || lsa->lsa_pool_name != NULL || - lsa->lsa_comp_end != 0 || lsa->lsa_pattern != 0); + return (lsa->lsa_stripe_size != LLAPI_LAYOUT_DEFAULT || + lsa->lsa_stripe_count != LLAPI_LAYOUT_DEFAULT || + lsa->lsa_stripe_off != LLAPI_LAYOUT_DEFAULT || + lsa->lsa_pattern != LLAPI_LAYOUT_RAID0 || + lsa->lsa_pool_name != NULL || + lsa->lsa_comp_end != 0); } +/** + * comp_args_to_layout() - Create or extend a composite layout. + * @composite: Pointer to the composite layout. + * @lsa: Stripe options for the new component. + * + * This function creates or extends a composite layout by adding a new + * component with stripe options from @lsa. + * + * Return: 0 on success or an error code on failure. + */ static int comp_args_to_layout(struct llapi_layout **composite, struct lfs_setstripe_args *lsa) { @@ -1138,13 +1519,13 @@ static int comp_args_to_layout(struct llapi_layout **composite, if (lsa->lsa_pattern == LLAPI_LAYOUT_MDT) { /* In case of Data-on-MDT patterns the only extra option * applicable is stripe size option. */ - if (lsa->lsa_stripe_count) { + if (lsa->lsa_stripe_count != LLAPI_LAYOUT_DEFAULT) { fprintf(stderr, "Option 'stripe-count' can't be " - "specified with Data-on-MDT component: %i\n", + "specified with Data-on-MDT component: %lld\n", lsa->lsa_stripe_count); return -EINVAL; } - if (lsa->lsa_stripe_size) { + if (lsa->lsa_stripe_size != LLAPI_LAYOUT_DEFAULT) { fprintf(stderr, "Option 'stripe-size' can't be " "specified with Data-on-MDT component: %llu\n", lsa->lsa_stripe_size); @@ -1156,9 +1537,9 @@ static int comp_args_to_layout(struct llapi_layout **composite, lsa->lsa_nr_osts); return -EINVAL; } - if (lsa->lsa_stripe_off != -1) { + if (lsa->lsa_stripe_off != LLAPI_LAYOUT_DEFAULT) { fprintf(stderr, "Option 'stripe-offset' can't be " - "specified with Data-on-MDT component: %i\n", + "specified with Data-on-MDT component: %lld\n", lsa->lsa_stripe_off); return -EINVAL; } @@ -1171,7 +1552,7 @@ static int comp_args_to_layout(struct llapi_layout **composite, rc = llapi_layout_pattern_set(layout, lsa->lsa_pattern); if (rc) { - fprintf(stderr, "Set stripe pattern %#x failed. %s\n", + fprintf(stderr, "Set stripe pattern %#llx failed. %s\n", lsa->lsa_pattern, strerror(errno)); return rc; } @@ -1179,26 +1560,18 @@ static int comp_args_to_layout(struct llapi_layout **composite, lsa->lsa_stripe_size = lsa->lsa_comp_end; } - if (lsa->lsa_stripe_size != 0) { - rc = llapi_layout_stripe_size_set(layout, - lsa->lsa_stripe_size); - if (rc) { - fprintf(stderr, "Set stripe size %llu failed. %s\n", - lsa->lsa_stripe_size, strerror(errno)); - return rc; - } + rc = llapi_layout_stripe_size_set(layout, lsa->lsa_stripe_size); + if (rc) { + fprintf(stderr, "Set stripe size %llu failed: %s\n", + lsa->lsa_stripe_size, strerror(errno)); + return rc; } - if (lsa->lsa_stripe_count != 0) { - rc = llapi_layout_stripe_count_set(layout, - lsa->lsa_stripe_count == -1 ? - LLAPI_LAYOUT_WIDE : - lsa->lsa_stripe_count); - if (rc) { - fprintf(stderr, "Set stripe count %d failed. %s\n", - lsa->lsa_stripe_count, strerror(errno)); - return rc; - } + rc = llapi_layout_stripe_count_set(layout, lsa->lsa_stripe_count); + if (rc) { + fprintf(stderr, "Set stripe count %lld failed: %s\n", + lsa->lsa_stripe_count, strerror(errno)); + return rc; } if (lsa->lsa_pool_name != NULL) { @@ -1208,12 +1581,21 @@ static int comp_args_to_layout(struct llapi_layout **composite, lsa->lsa_pool_name, strerror(errno)); return rc; } + } else { + rc = llapi_layout_pool_name_set(layout, ""); + if (rc) { + fprintf(stderr, "Clear pool name failed: %s\n", + strerror(errno)); + return rc; + } } if (lsa->lsa_nr_osts > 0) { if (lsa->lsa_stripe_count > 0 && + lsa->lsa_stripe_count != LLAPI_LAYOUT_DEFAULT && + lsa->lsa_stripe_count != LLAPI_LAYOUT_WIDE && lsa->lsa_nr_osts != lsa->lsa_stripe_count) { - fprintf(stderr, "stripe_count(%d) != nr_osts(%d)\n", + fprintf(stderr, "stripe_count(%lld) != nr_osts(%d)\n", lsa->lsa_stripe_count, lsa->lsa_nr_osts); return -EINVAL; } @@ -1223,7 +1605,7 @@ static int comp_args_to_layout(struct llapi_layout **composite, if (rc) break; } - } else if (lsa->lsa_stripe_off != -1) { + } else if (lsa->lsa_stripe_off != LLAPI_LAYOUT_DEFAULT) { rc = llapi_layout_ost_index_set(layout, 0, lsa->lsa_stripe_off); } if (rc) { @@ -1390,6 +1772,63 @@ static inline bool arg_is_eof(char *arg) !strncmp(arg, "eof", strlen("eof")); } +/** + * lfs_mirror_alloc() - Allocate a mirror argument structure. + * + * Return: Valid mirror_args pointer on success and + * NULL if memory allocation fails. + */ +static struct mirror_args *lfs_mirror_alloc(void) +{ + struct mirror_args *mirror = NULL; + + while (1) { + mirror = calloc(1, sizeof(*mirror)); + if (mirror != NULL) + break; + + sleep(1); + } + + return mirror; +} + +/** + * lfs_mirror_free() - Free memory allocated for a mirror argument + * structure. + * @mirror: Previously allocated mirror argument structure by + * lfs_mirror_alloc(). + * + * Free memory allocated for @mirror. + * + * Return: void. + */ +static void lfs_mirror_free(struct mirror_args *mirror) +{ + if (mirror->m_layout != NULL) + llapi_layout_free(mirror->m_layout); + free(mirror); +} + +/** + * lfs_mirror_list_free() - Free memory allocated for a mirror list. + * @mirror_list: Previously allocated mirror list. + * + * Free memory allocated for @mirror_list. + * + * Return: void. + */ +static void lfs_mirror_list_free(struct mirror_args *mirror_list) +{ + struct mirror_args *next_mirror = NULL; + + while (mirror_list != NULL) { + next_mirror = mirror_list->m_next; + lfs_mirror_free(mirror_list); + mirror_list = next_mirror; + } +} + enum { LFS_POOL_OPT = 3, LFS_COMP_COUNT_OPT, @@ -1398,11 +1837,13 @@ enum { LFS_COMP_DEL_OPT, LFS_COMP_SET_OPT, LFS_COMP_ADD_OPT, + LFS_COMP_USE_PARENT_OPT, + LFS_COMP_NO_VERIFY_OPT, LFS_PROJID_OPT, }; /* functions */ -static int lfs_setstripe(int argc, char **argv) +static int lfs_setstripe0(int argc, char **argv, enum setstripe_origin opc) { struct lfs_setstripe_args lsa; struct llapi_stripe_param *param = NULL; @@ -1426,8 +1867,15 @@ static int lfs_setstripe(int argc, char **argv) int comp_add = 0; __u32 comp_id = 0; struct llapi_layout *layout = NULL; - bool create_mirror = false; - const char *mirror_file = NULL; + struct llapi_layout **lpp = &layout; + bool mirror_mode = false; + bool has_m_file = false; + __u32 mirror_count = 0; + enum mirror_flags mirror_flags = 0; + struct mirror_args *mirror_list = NULL; + struct mirror_args *new_mirror = NULL; + struct mirror_args *last_mirror = NULL; + char cmd[PATH_MAX]; struct option long_opts[] = { /* --block is only valid in migrate mode */ @@ -1452,12 +1900,17 @@ static int lfs_setstripe(int argc, char **argv) { .val = LFS_COMP_SET_OPT, .name = "component-set", .has_arg = no_argument}, + { .val = LFS_COMP_USE_PARENT_OPT, + .name = "parent", .has_arg = no_argument}, + { .val = LFS_COMP_NO_VERIFY_OPT, + .name = "no-verify", .has_arg = no_argument}, { .val = 'c', .name = "stripe-count", .has_arg = required_argument}, { .val = 'c', .name = "stripe_count", .has_arg = required_argument}, { .val = 'd', .name = "delete", .has_arg = no_argument}, { .val = 'E', .name = "comp-end", .has_arg = required_argument}, { .val = 'E', .name = "component-end", .has_arg = required_argument}, + { .val = 'f', .name = "file", .has_arg = required_argument }, /* dirstripe {"mdt-hash", required_argument, 0, 'H'}, */ { .val = 'i', .name = "stripe-index", .has_arg = required_argument}, { .val = 'i', .name = "stripe_index", .has_arg = required_argument}, @@ -1467,7 +1920,7 @@ static int lfs_setstripe(int argc, char **argv) { .val = 'm', .name = "mdt", .has_arg = required_argument}, { .val = 'm', .name = "mdt-index", .has_arg = required_argument}, { .val = 'm', .name = "mdt_index", .has_arg = required_argument}, - { .val = 'M', .name = "mirror", .has_arg = optional_argument}, + { .val = 'N', .name = "mirror-count", .has_arg = optional_argument}, /* --non-block is only valid in migrate mode */ { .val = 'n', .name = "non-block", .has_arg = no_argument}, { .val = 'o', .name = "ost", .has_arg = required_argument}, @@ -1481,26 +1934,16 @@ static int lfs_setstripe(int argc, char **argv) /* dirstripe {"mdt-count", required_argument, 0, 'T'}, */ /* --verbose is only valid in migrate mode */ { .val = 'v', .name = "verbose", .has_arg = no_argument }, - { .val = LFS_COMP_ADD_OPT, - .name = "component-add", - .has_arg = no_argument }, - { .val = LFS_COMP_DEL_OPT, - .name = "component-del", - .has_arg = no_argument }, - { .val = LFS_COMP_FLAGS_OPT, - .name = "component-flags", - .has_arg = required_argument }, - { .val = LFS_COMP_SET_OPT, - .name = "component-set", - .has_arg = no_argument }, { .name = NULL } }; setstripe_args_init(&lsa); - if (strcmp(argv[0], "migrate") == 0) - migrate_mode = true; + migrate_mode = (opc == SO_MIGRATE); + mirror_mode = (opc == SO_MIRROR_CREATE || opc == SO_MIRROR_EXTEND); - while ((c = getopt_long(argc, argv, "bc:dE:i:I:m:no:p:L:s:S:v", + snprintf(cmd, sizeof(cmd), "%s %s", progname, argv[0]); + progname = cmd; + while ((c = getopt_long(argc, argv, "bc:dE:f:i:I:m:N::no:p:L:s:S:v", long_opts, NULL)) >= 0) { switch (c) { case 0: @@ -1520,6 +1963,18 @@ static int lfs_setstripe(int argc, char **argv) case LFS_COMP_SET_OPT: comp_set = 1; break; + case LFS_COMP_USE_PARENT_OPT: + if (!mirror_mode) { + fprintf(stderr, "error: %s: --parent must be " + "specified with --mirror-count|-N " + "option\n", progname); + goto usage_error; + } + setstripe_args_init(&lsa); + break; + case LFS_COMP_NO_VERIFY_OPT: + mirror_flags |= NO_VERIFY; + break; case 'b': if (!migrate_mode) { fprintf(stderr, @@ -1537,23 +1992,17 @@ static int lfs_setstripe(int argc, char **argv) progname, argv[0], optarg); goto usage_error; } + + if (lsa.lsa_stripe_count == -1) + lsa.lsa_stripe_count = LLAPI_LAYOUT_WIDE; break; case 'd': /* delete the default striping pattern */ delete = 1; break; - case 'M': - if (create_mirror) { - fprintf(stderr, "error: %s: --mirror can only " - "be specfied once", argv[0]); - goto error; - } - create_mirror = true; - mirror_file = optarg; - break; case 'E': if (lsa.lsa_comp_end != 0) { - result = comp_args_to_layout(&layout, &lsa); + result = comp_args_to_layout(lpp, &lsa); if (result) { fprintf(stderr, "%s %s: invalid layout\n", @@ -1561,7 +2010,7 @@ static int lfs_setstripe(int argc, char **argv) goto usage_error; } - setstripe_args_init(&lsa); + setstripe_args_init_inherit(&lsa); } if (arg_is_eof(optarg)) { @@ -1586,6 +2035,8 @@ static int lfs_setstripe(int argc, char **argv) progname, argv[0], optarg); goto usage_error; } + if (lsa.lsa_stripe_off == -1) + lsa.lsa_stripe_off = LLAPI_LAYOUT_DEFAULT; break; case 'I': comp_id = strtoul(optarg, &end, 0); @@ -1597,6 +2048,24 @@ static int lfs_setstripe(int argc, char **argv) goto usage_error; } break; + case 'f': + if (opc != SO_MIRROR_EXTEND) { + fprintf(stderr, + "error: %s: invalid option: %s\n", + progname, argv[optopt + 1]); + goto usage_error; + } + if (last_mirror == NULL) { + fprintf(stderr, "error: %s: '-N' must exist " + "in front of '%s'\n", + progname, argv[optopt + 1]); + goto usage_error; + } + + last_mirror->m_file = optarg; + last_mirror->m_count = 1; + has_m_file = true; + break; case 'L': if (strcmp(argv[optind - 1], "mdt") == 0) { /* Can be only the first component */ @@ -1639,6 +2108,48 @@ static int lfs_setstripe(int argc, char **argv) } migration_flags |= MIGRATION_NONBLOCK; break; + case 'N': + if (opc == SO_SETSTRIPE) { + opc = SO_MIRROR_CREATE; + mirror_mode = true; + } + mirror_count = 1; + if (optarg != NULL) { + mirror_count = strtoul(optarg, &end, 0); + if (*end != '\0' || mirror_count == 0) { + fprintf(stderr, + "error: %s: bad mirror count: %s\n", + progname, optarg); + result = -EINVAL; + goto error; + } + } + + new_mirror = lfs_mirror_alloc(); + new_mirror->m_count = mirror_count; + + if (mirror_list == NULL) + mirror_list = new_mirror; + + if (last_mirror != NULL) { + /* wrap up last mirror */ + if (lsa.lsa_comp_end == 0) + lsa.lsa_comp_end = LUSTRE_EOF; + + result = comp_args_to_layout(lpp, &lsa); + if (result) { + lfs_mirror_free(new_mirror); + goto error; + } + + setstripe_args_init_inherit(&lsa); + + last_mirror->m_next = new_mirror; + } + + last_mirror = new_mirror; + lpp = &last_mirror->m_layout; + break; case 'o': lsa.lsa_nr_osts = parse_targets(osts, sizeof(osts) / sizeof(__u32), @@ -1651,7 +2162,7 @@ static int lfs_setstripe(int argc, char **argv) } lsa.lsa_osts = osts; - if (lsa.lsa_stripe_off == -1) + if (lsa.lsa_stripe_off == LLAPI_LAYOUT_DEFAULT) lsa.lsa_stripe_off = osts[0]; break; case 'p': @@ -1693,28 +2204,41 @@ static int lfs_setstripe(int argc, char **argv) goto usage_error; } - if (create_mirror) { - if (!comp_add) { - fprintf(stderr, "error: %s: --component-add must be " - "specified with --mirror option\n", argv[0]); - goto error; - } + if (mirror_mode && mirror_count == 0) { + fprintf(stderr, + "error: %s: --mirror-count|-N option is required\n", + progname); + result = -EINVAL; + goto error; + } + + if (mirror_mode) { if (lsa.lsa_comp_end == 0) lsa.lsa_comp_end = LUSTRE_EOF; - if (lsa.lsa_comp_end != LUSTRE_EOF) { - fprintf(stderr, - "error: %s: creating non-eof ending mirror\n", - argv[0]); - goto error; - } } if (lsa.lsa_comp_end != 0) { - result = comp_args_to_layout(&layout, &lsa); + result = comp_args_to_layout(lpp, &lsa); if (result) goto error; } + if (mirror_flags & NO_VERIFY) { + if (opc != SO_MIRROR_EXTEND) { + fprintf(stderr, + "error: %s: --no-verify is valid only for lfs mirror extend command\n", + progname); + result = -EINVAL; + goto error; + } else if (!has_m_file) { + fprintf(stderr, + "error: %s: --no-verify must be specified with -f option\n", + progname); + result = -EINVAL; + goto error; + } + } + /* Only LCME_FL_INIT flags is used in PFL, and it shouldn't be * altered by user space tool, so we don't need to support the * --component-set for this moment. */ @@ -1764,6 +2288,13 @@ static int lfs_setstripe(int argc, char **argv) progname); goto usage_error; } + + if (mirror_mode) { + fprintf(stderr, "error: %s: can't use --component-add " + "or --component-del for mirror operation\n", + progname); + goto usage_error; + } } if (comp_add) { @@ -1773,13 +2304,12 @@ static int lfs_setstripe(int argc, char **argv) progname, argv[0]); goto usage_error; } - if (!create_mirror) { - result = adjust_first_extent(fname, layout); - if (result == -ENODATA) - comp_add = 0; - else if (result != 0) - goto error; - } + + result = adjust_first_extent(fname, layout); + if (result == -ENODATA) + comp_add = 0; + else if (result != 0) + goto error; } if (mdt_idx_arg != NULL && optind > 3) { @@ -1824,17 +2354,28 @@ static int lfs_setstripe(int argc, char **argv) goto error; } - param->lsp_stripe_size = lsa.lsa_stripe_size; - param->lsp_stripe_offset = lsa.lsa_stripe_off; - param->lsp_stripe_count = lsa.lsa_stripe_count; + if (lsa.lsa_stripe_size != LLAPI_LAYOUT_DEFAULT) + param->lsp_stripe_size = lsa.lsa_stripe_size; + if (lsa.lsa_stripe_count != LLAPI_LAYOUT_DEFAULT) { + if (lsa.lsa_stripe_count == LLAPI_LAYOUT_WIDE) + param->lsp_stripe_count = -1; + else + param->lsp_stripe_count = lsa.lsa_stripe_count; + } + if (lsa.lsa_stripe_off == LLAPI_LAYOUT_DEFAULT) + param->lsp_stripe_offset = -1; + else + param->lsp_stripe_offset = lsa.lsa_stripe_off; param->lsp_pool = lsa.lsa_pool_name; param->lsp_is_specific = false; if (lsa.lsa_nr_osts > 0) { if (lsa.lsa_stripe_count > 0 && + lsa.lsa_stripe_count != LLAPI_LAYOUT_DEFAULT && + lsa.lsa_stripe_count != LLAPI_LAYOUT_WIDE && lsa.lsa_nr_osts != lsa.lsa_stripe_count) { - fprintf(stderr, - "%s %s: stripe count '%d' does not match number of OSTs: %d\n", - progname, argv[0], lsa.lsa_stripe_count, + fprintf(stderr, "error: %s: stripe count %lld " + "doesn't match the number of OSTs: %d\n" + , argv[0], lsa.lsa_stripe_count, lsa.lsa_nr_osts); free(param); goto usage_error; @@ -1860,11 +2401,12 @@ static int lfs_setstripe(int argc, char **argv) result = lfs_component_del(fname, comp_id, lsa.lsa_comp_flags); } else if (comp_add != 0) { - if (create_mirror) - result = lfs_create_mirror(fname, layout, - mirror_file); - else - result = lfs_component_add(fname, layout); + result = lfs_component_add(fname, layout); + } else if (opc == SO_MIRROR_CREATE) { + result = mirror_create(fname, mirror_list); + } else if (opc == SO_MIRROR_EXTEND) { + result = mirror_extend(fname, mirror_list, + mirror_flags); } else if (layout != NULL) { result = lfs_component_create(fname, O_CREAT | O_WRONLY, 0644, layout); @@ -1891,11 +2433,13 @@ static int lfs_setstripe(int argc, char **argv) free(param); llapi_layout_free(layout); + lfs_mirror_list_free(mirror_list); return result2; usage_error: result = CMD_HELP; error: llapi_layout_free(layout); + lfs_mirror_list_free(mirror_list); return result; } @@ -5594,6 +6138,55 @@ next: return rc; } +/** + * lfs_mirror() - Parse and execute lfs mirror commands. + * @argc: The count of lfs mirror command line arguments. + * @argv: Array of strings for lfs mirror command line arguments. + * + * This function parses lfs mirror commands and performs the + * corresponding functions specified in mirror_cmdlist[]. + * + * Return: 0 on success or an error code on failure. + */ +static int lfs_mirror(int argc, char **argv) +{ + char cmd[PATH_MAX]; + int rc = 0; + + setlinebuf(stdout); + + Parser_init("lfs-mirror > ", mirror_cmdlist); + + snprintf(cmd, sizeof(cmd), "%s %s", progname, argv[0]); + progname = cmd; + program_invocation_short_name = cmd; + if (argc > 1) + rc = Parser_execarg(argc - 1, argv + 1, mirror_cmdlist); + else + rc = Parser_commands(); + + return rc < 0 ? -rc : rc; +} + +/** + * lfs_mirror_list_commands() - List lfs mirror commands. + * @argc: The count of command line arguments. + * @argv: Array of strings for command line arguments. + * + * This function lists lfs mirror commands defined in mirror_cmdlist[]. + * + * Return: 0 on success. + */ +static int lfs_mirror_list_commands(int argc, char **argv) +{ + char buffer[81] = ""; + + Parser_list_commands(mirror_cmdlist, buffer, sizeof(buffer), + NULL, 0, 4); + + return 0; +} + static int lfs_list_commands(int argc, char **argv) { char buffer[81] = ""; /* 80 printable chars + terminating NUL */ diff --git a/lustre/utils/liblustreapi_layout.c b/lustre/utils/liblustreapi_layout.c index 2ff37b6..ae2826e 100644 --- a/lustre/utils/liblustreapi_layout.c +++ b/lustre/utils/liblustreapi_layout.c @@ -70,6 +70,7 @@ struct llapi_layout { uint32_t llot_gen; uint32_t llot_flags; bool llot_is_composite; + uint16_t llot_mirror_count; /* Cursor pointing to one of the components in llot_comp_list */ struct llapi_layout_comp *llot_cur_comp; struct list_head llot_comp_list; @@ -317,6 +318,7 @@ static struct llapi_layout *__llapi_layout_alloc(void) layout->llot_gen = 0; layout->llot_flags = 0; layout->llot_is_composite = false; + layout->llot_mirror_count = 1; layout->llot_cur_comp = NULL; INIT_LIST_HEAD(&layout->llot_comp_list); @@ -377,7 +379,9 @@ llapi_layout_from_lum(const struct lov_user_md *lum, int lum_size) if (lum->lmm_magic == LOV_MAGIC_COMP_V1) { comp_v1 = (struct lov_comp_md_v1 *)lum; ent_count = comp_v1->lcm_entry_count; + layout->llot_gen = comp_v1->lcm_layout_gen; layout->llot_is_composite = true; + layout->llot_mirror_count = comp_v1->lcm_mirror_count + 1; layout->llot_gen = comp_v1->lcm_layout_gen; layout->llot_flags = comp_v1->lcm_flags; } else if (lum->lmm_magic == LOV_MAGIC_V1 || @@ -506,7 +510,7 @@ llapi_layout_to_lum(const struct llapi_layout *layout) comp_cnt++; lum_size = sizeof(*comp_v1) + comp_cnt * sizeof(*ent); - lum = malloc(lum_size); + lum = calloc(lum_size, 1); if (lum == NULL) { errno = ENOMEM; return NULL; @@ -517,7 +521,7 @@ llapi_layout_to_lum(const struct llapi_layout *layout) comp_v1->lcm_layout_gen = 0; comp_v1->lcm_flags = layout->llot_flags; comp_v1->lcm_entry_count = comp_cnt; - comp_v1->lcm_mirror_count = 0; + comp_v1->lcm_mirror_count = layout->llot_mirror_count - 1; offset += lum_size; } @@ -567,8 +571,6 @@ llapi_layout_to_lum(const struct llapi_layout *layout) blob->lmm_magic = magic; if (pattern == LLAPI_LAYOUT_DEFAULT) - blob->lmm_pattern = 0; - else if (pattern == LLAPI_LAYOUT_RAID0) blob->lmm_pattern = LOV_PATTERN_RAID0; else if (pattern == LLAPI_LAYOUT_MDT) blob->lmm_pattern = LOV_PATTERN_MDT; @@ -734,7 +736,7 @@ static bool is_any_specified(const struct llapi_layout *layout) if (comp == NULL) return false; - if (layout->llot_is_composite) + if (layout->llot_is_composite || layout->llot_mirror_count != 1) return true; return comp->llc_pattern != LLAPI_LAYOUT_DEFAULT || @@ -1227,8 +1229,7 @@ int llapi_layout_pattern_set(struct llapi_layout *layout, uint64_t pattern) return -1; if (pattern != LLAPI_LAYOUT_DEFAULT && - pattern != LLAPI_LAYOUT_RAID0 && - pattern != LLAPI_LAYOUT_MDT) { + pattern != LLAPI_LAYOUT_RAID0 && pattern != LLAPI_LAYOUT_MDT) { errno = EOPNOTSUPP; return -1; } @@ -1537,6 +1538,67 @@ int llapi_layout_flags_set(struct llapi_layout *layout, uint32_t flags) } /** + * llapi_layout_mirror_count_is_valid() - Check the validity of mirror count. + * @count: Mirror count value to be checked. + * + * This function checks the validity of mirror count. + * + * Return: true on success or false on failure. + */ +static bool llapi_layout_mirror_count_is_valid(uint16_t count) +{ + return count >= 0 && count <= LUSTRE_MIRROR_COUNT_MAX; +} + +/** + * llapi_layout_mirror_count_get() - Get mirror count from the header of + * a layout. + * @layout: Layout to get mirror count from. + * @count: Returned mirror count value. + * + * This function gets mirror count from the header of a layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_mirror_count_get(struct llapi_layout *layout, + uint16_t *count) +{ + if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) { + errno = EINVAL; + return -1; + } + + *count = layout->llot_mirror_count; + return 0; +} + +/** + * llapi_layout_mirror_count_set() - Set mirror count to the header of a layout. + * @layout: Layout to set mirror count in. + * @count: Mirror count value to be set. + * + * This function sets mirror count to the header of a layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_mirror_count_set(struct llapi_layout *layout, + uint16_t count) +{ + if (layout->llot_magic != LLAPI_LAYOUT_MAGIC) { + errno = EINVAL; + return -1; + } + + if (!llapi_layout_mirror_count_is_valid(count)) { + errno = EINVAL; + return -1; + } + + layout->llot_mirror_count = count; + return 0; +} + +/** * Fetch the start and end offset of the current layout component. * * \param[in] layout the layout component @@ -1772,12 +1834,6 @@ int llapi_layout_comp_add(struct llapi_layout *layout) last = list_entry(layout->llot_comp_list.prev, typeof(*last), llc_list); - /* Inherit some attributes from existing component */ - new->llc_stripe_size = comp->llc_stripe_size; - new->llc_stripe_count = comp->llc_stripe_count; - if (comp->llc_pool_name[0] != '\0') - strncpy(new->llc_pool_name, comp->llc_pool_name, - sizeof(comp->llc_pool_name)); if (new->llc_extent.e_end <= last->llc_extent.e_end) { __llapi_comp_free(new); errno = EINVAL; @@ -2083,3 +2139,78 @@ bool llapi_layout_is_composite(struct llapi_layout *layout) { return layout->llot_is_composite; } + +/** + * llapi_layout_merge() - Merge a composite layout into another one. + * @dst_layout: Destination composite layout. + * @src_layout: Source composite layout. + * + * This function copies all of the components from @src_layout and + * appends them to @dst_layout. + * + * Return: 0 on success or -1 on failure. + */ +int llapi_layout_merge(struct llapi_layout **dst_layout, + const struct llapi_layout *src_layout) +{ + struct llapi_layout *new_layout = *dst_layout; + struct llapi_layout_comp *new = NULL; + struct llapi_layout_comp *comp = NULL; + int i = 0; + + if (src_layout == NULL || + list_empty((struct list_head *)&src_layout->llot_comp_list)) + return 0; + + if (new_layout == NULL) { + new_layout = __llapi_layout_alloc(); + if (new_layout == NULL) { + errno = ENOMEM; + return -1; + } + } + + list_for_each_entry(comp, &src_layout->llot_comp_list, llc_list) { + new = __llapi_comp_alloc(0); + if (new == NULL) { + errno = ENOMEM; + goto error; + } + + new->llc_pattern = comp->llc_pattern; + new->llc_stripe_size = comp->llc_stripe_size; + new->llc_stripe_count = comp->llc_stripe_count; + new->llc_stripe_offset = comp->llc_stripe_offset; + + if (comp->llc_pool_name[0] != '\0') + strncpy(new->llc_pool_name, comp->llc_pool_name, + sizeof(new->llc_pool_name)); + + for (i = 0; i < comp->llc_objects_count; i++) { + if (__llapi_comp_objects_realloc(new, + stripe_number_roundup(i)) < 0) { + errno = EINVAL; + __llapi_comp_free(new); + goto error; + } + new->llc_objects[i].l_ost_idx = \ + comp->llc_objects[i].l_ost_idx; + } + + new->llc_objects_count = comp->llc_objects_count; + new->llc_extent.e_start = comp->llc_extent.e_start; + new->llc_extent.e_end = comp->llc_extent.e_end; + new->llc_id = comp->llc_id; + new->llc_flags = comp->llc_flags; + + list_add_tail(&new->llc_list, &new_layout->llot_comp_list); + new_layout->llot_cur_comp = new; + } + new_layout->llot_is_composite = true; + + *dst_layout = new_layout; + return 0; +error: + llapi_layout_free(new_layout); + return -1; +}