From 6460ae59bf6e5175797dc66ecbe560eebc8b6333 Mon Sep 17 00:00:00 2001 From: Gregoire Pichon Date: Mon, 28 Jul 2014 14:16:33 +0200 Subject: [PATCH] LU-5319 utils: update lr_reader to display additional data The lr_reader command currently displays only the server area of the LAST_RCVD file of a Lustre ldiskfs target device. This patch enhances the command to allow diplaying : - the per-client area of the LAST_RCVD file when --client or -c option is specified - the reply data of the REPLY_DATA file when --reply or -r option is specified It still only support ldiskfs target device. Signed-off-by: Gregoire Pichon Change-Id: Ifc2b7f9591eef0da5e4a5dc00f37e58484d4ea97 Reviewed-on: http://review.whamcloud.com/14862 Tested-by: Jenkins Reviewed-by: Alex Zhuravlev Tested-by: Maloo Reviewed-by: Jian Yu Reviewed-by: Andreas Dilger --- lustre/tests/sanity.sh | 52 +++++ lustre/tests/test-framework.sh | 3 + lustre/utils/lr_reader.c | 457 ++++++++++++++++++++++++++++------------- 3 files changed, 367 insertions(+), 145 deletions(-) diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index ab8b62d..e0a1b97 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -13137,6 +13137,58 @@ test_251() { } run_test 251 "Handling short read and write correctly" +test_252() { + local tgt + local dev + local out + local uuid + local num + local gen + + if [ "$(facet_fstype ost1)" != "ldiskfs" -o \ + "$(facet_fstype mds1)" != "ldiskfs" ]; then + skip "can only run lr_reader on ldiskfs target" + return + fi + + # check lr_reader on OST0000 + tgt=ost1 + dev=$(facet_device $tgt) + out=$(do_facet $tgt $LR_READER $dev) + [ $? -eq 0 ] || error "$LR_READER failed on target $tgt device $dev" + echo "$out" + uuid=$(echo "$out" | grep -i uuid | awk '{ print $2 }') + [ "$uuid" == "$(ostuuid_from_index 0)" ] || + error "Invalid uuid returned by $LR_READER on target $tgt" + echo -e "uuid returned by $LR_READER is '$uuid'\n" + + # check lr_reader -c on MDT0000 + tgt=mds1 + dev=$(facet_device $tgt) + if ! do_facet $tgt $LR_READER -h | grep -q OPTIONS; then + echo "$LR_READER does not support additional options" + return 0 + fi + out=$(do_facet $tgt $LR_READER -c $dev) + [ $? -eq 0 ] || error "$LR_READER failed on target $tgt device $dev" + echo "$out" + num=$(echo "$out" | grep -c "mdtlov") + [ "$num" -eq $((MDSCOUNT - 1)) ] || + error "Invalid number of mdtlov clients returned by $LR_READER" + echo -e "Number of mdtlov clients returned by $LR_READER is '$num'\n" + + # check lr_reader -cr on MDT0000 + out=$(do_facet $tgt $LR_READER -cr $dev) + [ $? -eq 0 ] || error "$LR_READER failed on target $tgt device $dev" + echo "$out" + echo "$out" | grep -q "^reply_data:$" || + error "$LR_READER should have returned 'reply_data' section" + num=$(echo "$out" | grep -c "client_generation") + echo -e "Number of reply data returned by $LR_READER is '$num'\n" +} +run_test 252 "check lr_reader tool" + + cleanup_test_300() { trap 0 umask $SAVE_UMASK diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index fdfa7b0..197ff9e 100755 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -268,6 +268,9 @@ init_test_env() { export LFS_MIGRATE=${LFS_MIGRATE:-$LUSTRE/scripts/lfs_migrate} [ ! -f "$LFS_MIGRATE" ] && export LFS_MIGRATE=$(which lfs_migrate 2> /dev/null) + export LR_READER=${LR_READER:-"$LUSTRE/utils/lr_reader"} + [ ! -f "$LR_READER" ] && export LR_READER=$(which lr_reader 2> /dev/null) + [ -z "$LR_READER" ] && export LR_READER="/usr/sbin/lr_reader" export NAME=${NAME:-local} export LGSSD=${LGSSD:-"$LUSTRE/utils/gss/lgssd"} [ "$GSS_PIPEFS" = "true" ] && [ ! -f "$LGSSD" ] && \ diff --git a/lustre/utils/lr_reader.c b/lustre/utils/lr_reader.c index 6eb2cba..dff6de3 100644 --- a/lustre/utils/lr_reader.c +++ b/lustre/utils/lr_reader.c @@ -63,164 +63,331 @@ #include #include -int run_command(char *cmd) +char *progname; +static struct option const longopts[] = { + { "help", no_argument, 0, 'h' }, + { "client", no_argument, 0, 'c' }, + { "reply", no_argument, 0, 'r' }, + { 0, 0, 0, 0} +}; + +/* Executes the command \a cmd and returns command status. + */ +int run_command(char *cmd, size_t cmdsz) { - char log[] = "/tmp/mkfs_logXXXXXX"; - int fd, rc; - - - if ((fd = mkstemp(log)) >= 0) { - close(fd); - strcat(cmd, " >"); - strcat(cmd, log); - } - strcat(cmd, " 2>&1"); - - /* Can't use popen because we need the rv of the command */ - rc = system(cmd); - if (rc && fd >= 0) { - char buf[128]; - FILE *fp; - fp = fopen(log, "r"); - if (fp) { - while (fgets(buf, sizeof(buf), fp) != NULL) { - if (rc) - printf(" %s", buf); - } - fclose(fp); - } - } - if (fd >= 0) - remove(log); - return rc; -} + char log[] = "/tmp/run_command_logXXXXXX"; + int fd, rc; + + if (strlen(cmd) + strlen(log) + 8 > cmdsz) { + fprintf(stderr, "Command buffer overflow: %.*s...\n", + (int)cmdsz, cmd); + return -ENOMEM; + } + + fd = mkstemp(log); + if (fd >= 0) { + close(fd); + strncat(cmd, " >", 2); + strncat(cmd, log, strlen(log)); + } + strncat(cmd, " 2>&1", 5); + + /* Can't use popen because we need the rv of the command */ + rc = system(cmd); + if (rc && fd >= 0) { + char buf[128]; + FILE *fp; + fp = fopen(log, "r"); + if (fp) { + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (rc) + printf(" %s", buf); + } + fclose(fp); + } + } + if (fd >= 0) + remove(log); + return rc; +} + +void display_usage(void) +{ + printf("Usage: %s [OPTIONS] devicename\n", progname); + printf("Read and print the last_rcvd file from a device\n"); + printf("(safe for mounted devices)\n"); + printf("\t-c, --client, display client information\n"); + printf("\t-h, --help, display this help and exit\n"); + printf("\t-r, --reply, display reply data information\n"); +} int main(int argc, char *const argv[]) { - char tmpdir[] = "/tmp/dirXXXXXX"; - char cmd[128]; - char filepnm[128]; - char *progname, *dev; - struct lr_server_data lsd; - FILE *filep; - int ret; - - if ((argc < 2) || (argv[argc - 1][0] == '-')) { - printf("Usage: %s devicename\n", argv[0]); - printf("Read and print the last_rcvd file from a device\n"); - printf("(safe for mounted devices)\n"); - return EINVAL; - } - - progname = argv[0]; - dev = argv[argc - 1]; - - /* Make a temporary directory to hold Lustre data files. */ - if (!mkdtemp(tmpdir)) { - fprintf(stderr, "%s: Can't create temporary directory %s: %s\n", - progname, tmpdir, strerror(errno)); - return errno; - } - - memset(cmd, 0, sizeof(cmd)); + char tmpdir[] = "/tmp/dirXXXXXX"; + char cmd[128]; + char filepnm[128] = ""; + char *dev; + struct lr_server_data lsd; + FILE *filep = NULL; + int ret; + int c; + int opt_client = 0; + int opt_reply = 0; + + progname = argv[0]; + while ((c = getopt_long(argc, argv, "chr", longopts, NULL)) != -1) { + switch (c) { + case 'c': + opt_client = 1; + break; + case 'r': + opt_reply = 1; + break; + case 'h': + default: + display_usage(); + return -1; + } + } + dev = argv[optind]; + if (!dev) { + display_usage(); + return -1; + } + + /* Make a temporary directory to hold Lustre data files. */ + if (!mkdtemp(tmpdir)) { + fprintf(stderr, "%s: Can't create temporary directory %s: %s\n", + progname, tmpdir, strerror(errno)); + return errno; + } + + memset(cmd, 0, sizeof(cmd)); snprintf(cmd, sizeof(cmd), - "%s -c -R 'dump /%s %s/%s' %s", - DEBUGFS, LAST_RCVD, tmpdir, LAST_RCVD, dev); - - ret = run_command(cmd); - if (ret) { - fprintf(stderr, "%s: Unable to dump %s file\n", - progname, LAST_RCVD); - goto out_rmdir; - } - - sprintf(filepnm, "%s/%s", tmpdir, LAST_RCVD); - filep = fopen(filepnm, "r"); - if (!filep) { - fprintf(stderr, "%s: Unable to read old data\n", - progname); - ret = -errno; - goto out_rmdir; - } - - printf("Reading %s\n", LAST_RCVD); - ret = fread(&lsd, 1, sizeof(lsd), filep); - if (ret < sizeof(lsd)) { - fprintf(stderr, "%s: Short read (%d of %d)\n", - progname, ret, (int)sizeof(lsd)); - ret = -ferror(filep); - if (ret) - goto out_close; - } - -#if 0 - __u8 lsd_uuid[40]; /* server UUID */ - __u64 lsd_last_transno; /* last completed transaction ID */ - __u64 lsd_compat14; /* reserved - compat with old last_rcvd */ - __u64 lsd_mount_count; /* incarnation number */ - __u32 lsd_feature_compat; /* compatible feature flags */ - __u32 lsd_feature_rocompat;/* read-only compatible feature flags */ - __u32 lsd_feature_incompat;/* incompatible feature flags */ - __u32 lsd_server_size; /* size of server data area */ - __u32 lsd_client_start; /* start of per-client data area */ - __u16 lsd_client_size; /* size of per-client data area */ - __u16 lsd_subdir_count; /* number of subdirectories for objects */ - __u64 lsd_catalog_oid; /* recovery catalog object id */ - __u32 lsd_catalog_ogen; /* recovery catalog inode generation */ - __u8 lsd_peeruuid[40]; /* UUID of MDS associated with this OST */ - __u32 lsd_osd_index; /* index number of OST/MDT in LOV/LMV */ - __u8 lsd_padding[LR_SERVER_SIZE - 148]; -#endif + "%s -c -R 'dump /%s %s/%s' %s", + DEBUGFS, LAST_RCVD, tmpdir, LAST_RCVD, dev); + + ret = run_command(cmd, sizeof(cmd)); + if (ret) { + fprintf(stderr, "%s: Unable to dump %s file\n", + progname, LAST_RCVD); + goto out_rmdir; + } + + snprintf(filepnm, 128, "%s/%s", tmpdir, LAST_RCVD); + filep = fopen(filepnm, "r"); + if (!filep) { + fprintf(stderr, "%s: Unable to read old data\n", + progname); + ret = -errno; + goto out_rmdir; + } + unlink(filepnm); + + /* read lr_server_data structure */ + printf("%s:\n", LAST_RCVD); + ret = fread(&lsd, 1, sizeof(lsd), filep); + if (ret < sizeof(lsd)) { + fprintf(stderr, "%s: Short read (%d of %d)\n", + progname, ret, (int)sizeof(lsd)); + ret = -ferror(filep); + if (ret) + goto out_close; + } + + /* swab structure fields of interest */ + lsd.lsd_feature_compat = le32_to_cpu(lsd.lsd_feature_compat); + lsd.lsd_feature_incompat = le32_to_cpu(lsd.lsd_feature_incompat); + lsd.lsd_feature_rocompat = le32_to_cpu(lsd.lsd_feature_rocompat); + lsd.lsd_last_transno = le64_to_cpu(lsd.lsd_last_transno); + lsd.lsd_osd_index = le32_to_cpu(lsd.lsd_osd_index); + lsd.lsd_mount_count = le64_to_cpu(lsd.lsd_mount_count); + + /* display */ + printf(" uuid: %.40s\n", lsd.lsd_uuid); + printf(" feature_compat: %#x\n", lsd.lsd_feature_compat); + printf(" feature_incompat: %#x\n", lsd.lsd_feature_incompat); + printf(" feature_rocompat: %#x\n", lsd.lsd_feature_rocompat); + printf(" last_transaction: %llu\n", lsd.lsd_last_transno); + printf(" target_index: %u\n", lsd.lsd_osd_index); + printf(" mount_count: %llu\n", lsd.lsd_mount_count); - printf("UUID %s\n", lsd.lsd_uuid); - printf("Feature compat=%#x\n", lsd.lsd_feature_compat); - printf("Feature incompat=%#x\n", lsd.lsd_feature_incompat); - printf("Feature rocompat=%#x\n", lsd.lsd_feature_rocompat); - printf("Last transaction %llu\n", (long long)lsd.lsd_last_transno); - printf("target index %u\n", lsd.lsd_osd_index); - - if ((lsd.lsd_feature_compat & OBD_COMPAT_OST) || - (lsd.lsd_feature_incompat & OBD_INCOMPAT_OST)) { - printf("OST, index %d\n", lsd.lsd_osd_index); - } else if ((lsd.lsd_feature_compat & OBD_COMPAT_MDT) || - (lsd.lsd_feature_incompat & OBD_INCOMPAT_MDT)) { - /* We must co-locate so mgs can see old logs. - If user doesn't want this, they can copy the old - logs manually and re-tunefs. */ - printf("MDS, index %d\n", lsd.lsd_osd_index); - } else { - /* If neither is set, we're pre-1.4.6, make a guess. */ - /* Construct debugfs command line. */ - memset(cmd, 0, sizeof(cmd)); - snprintf(cmd, sizeof(cmd), "%s -c -R 'rdump /%s %s' %s", - DEBUGFS, MDT_LOGS_DIR, tmpdir, dev); - - run_command(cmd); - - sprintf(filepnm, "%s/%s", tmpdir, MDT_LOGS_DIR); - if (lsd.lsd_osd_index > 0) { - printf("non-flagged OST, index %d\n", - lsd.lsd_osd_index); - } else { - /* If there's a LOGS dir, it's an MDT */ - if ((ret = access(filepnm, F_OK)) == 0) { - /* Old MDT's are always index 0 - (pre CMD) */ - printf("non-flagged MDS, index 0\n"); - } else { - printf("non-flagged OST, index unknown\n"); + /* read client information */ + if (opt_client) { + lsd.lsd_client_start = le32_to_cpu(lsd.lsd_client_start); + lsd.lsd_client_size = le16_to_cpu(lsd.lsd_client_size); + printf(" client_area_start: %u\n", lsd.lsd_client_start); + printf(" client_area_size: %hu\n", lsd.lsd_client_size); + + /* seek to per-client data area */ + ret = fseek(filep, lsd.lsd_client_start, SEEK_SET); + if (ret) { + fprintf(stderr, "%s: seek failed. %s\n", + progname, strerror(errno)); + ret = errno; + goto out_close; + } + + /* walk throuh the per-client data area */ + while (true) { + struct lsd_client_data lcd; + + /* read a per-client data area */ + ret = fread(&lcd, 1, sizeof(lcd), filep); + if (ret < sizeof(lcd)) { + if (feof(filep)) + break; + fprintf(stderr, "%s: Short read (%d of %d)\n", + progname, ret, (int)sizeof(lcd)); + ret = -ferror(filep); + goto out_close; + } + + if (lcd.lcd_uuid[0] == '\0') + continue; + + /* swab structure fields */ + lcd.lcd_last_transno = + le64_to_cpu(lcd.lcd_last_transno); + lcd.lcd_last_xid = le64_to_cpu(lcd.lcd_last_xid); + lcd.lcd_last_result = le32_to_cpu(lcd.lcd_last_result); + lcd.lcd_last_data = le32_to_cpu(lcd.lcd_last_data); + lcd.lcd_generation = le32_to_cpu(lcd.lcd_generation); + + /* display per-client data area */ + printf("\n %.40s:\n", lcd.lcd_uuid); + printf(" generation: %u\n", lcd.lcd_generation); + printf(" last_transaction: %llu\n", + lcd.lcd_last_transno); + printf(" last_xid: %llu\n", lcd.lcd_last_xid); + printf(" last_result: %u\n", lcd.lcd_last_result); + printf(" last_data: %u\n", lcd.lcd_last_data); + + if (lcd.lcd_last_close_transno != 0 && + lcd.lcd_last_close_xid != 0) { + lcd.lcd_last_close_transno = + le64_to_cpu(lcd.lcd_last_close_transno); + lcd.lcd_last_close_xid = + le64_to_cpu(lcd.lcd_last_close_xid); + lcd.lcd_last_close_result = + le32_to_cpu(lcd.lcd_last_close_result); + lcd.lcd_last_close_data = + le32_to_cpu(lcd.lcd_last_close_data); + printf(" last_close_transation: %llu\n", + lcd.lcd_last_close_transno); + printf(" last_close_xid: %llu\n", + lcd.lcd_last_close_xid); + printf(" last_close_result: %u\n", + lcd.lcd_last_close_result); + printf(" last_close_data: %u\n", + lcd.lcd_last_close_data); } } } - -out_close: fclose(filep); + filep = NULL; + + /* read reply data information */ + if (opt_reply) { + struct lsd_reply_header lrh; + struct lsd_reply_data lrd; + unsigned long long slot; + + snprintf(cmd, sizeof(cmd), + "%s -c -R 'dump /%s %s/%s' %s", + DEBUGFS, REPLY_DATA, tmpdir, REPLY_DATA, dev); + + ret = run_command(cmd, sizeof(cmd)); + if (ret) { + fprintf(stderr, "%s: Unable to dump %s file\n", + progname, REPLY_DATA); + goto out_rmdir; + } + + snprintf(filepnm, sizeof(filepnm), + "%s/%s", tmpdir, REPLY_DATA); + filep = fopen(filepnm, "r"); + if (!filep) { + fprintf(stderr, "%s: Unable to read reply data\n", + progname); + ret = -errno; + goto out_rmdir; + } + unlink(filepnm); + + /* read reply_data header */ + printf("\n%s:\n", REPLY_DATA); + ret = fread(&lrh, 1, sizeof(lrh), filep); + if (ret < sizeof(lrh)) { + fprintf(stderr, "%s: Short read (%d of %d)\n", + progname, ret, (int)sizeof(lrh)); + ret = -ferror(filep); + if (ret) + goto out_close; + } + + /* check header */ + lrh.lrh_magic = le32_to_cpu(lrh.lrh_magic); + lrh.lrh_header_size = le32_to_cpu(lrh.lrh_header_size); + lrh.lrh_reply_size = le32_to_cpu(lrh.lrh_reply_size); + if (lrh.lrh_magic != LRH_MAGIC) { + fprintf(stderr, "%s: invalid %s header: " + "lrh_magic=%08x expected %08x\n", + progname, REPLY_DATA, lrh.lrh_magic, LRH_MAGIC); + goto out_close; + } + if (lrh.lrh_header_size != sizeof(struct lsd_reply_header)) { + fprintf(stderr, "%s: invalid %s header: " + "lrh_header_size=%08x expected %08x\n", + progname, REPLY_DATA, lrh.lrh_header_size, + (unsigned int)sizeof(struct lsd_reply_header)); + goto out_close; + } + if (lrh.lrh_reply_size != sizeof(struct lsd_reply_data)) { + fprintf(stderr, "%s: invalid %s header: " + "lrh_reply_size=%08x expected %08x\n", + progname, REPLY_DATA, lrh.lrh_reply_size, + (unsigned int)sizeof(struct lsd_reply_data)); + goto out_close; + } + + /* walk throuh the reply data */ + for (slot = 0; ; slot++) { + /* read a reply data */ + ret = fread(&lrd, 1, sizeof(lrd), filep); + if (ret < sizeof(lrd)) { + if (feof(filep)) + break; + fprintf(stderr, "%s: Short read (%d of %d)\n", + progname, ret, (int)sizeof(lrd)); + ret = -ferror(filep); + goto out_close; + } + + /* display reply data */ + lrd.lrd_transno = le64_to_cpu(lrd.lrd_transno); + lrd.lrd_xid = le64_to_cpu(lrd.lrd_xid); + lrd.lrd_data = le64_to_cpu(lrd.lrd_data); + lrd.lrd_result = le32_to_cpu(lrd.lrd_result); + lrd.lrd_client_gen = le32_to_cpu(lrd.lrd_client_gen); + + printf(" %lld:\n", slot); + printf(" client_generation: %u\n", + lrd.lrd_client_gen); + printf(" last_transaction: %llu\n", lrd.lrd_transno); + printf(" last_xid: %llu\n", lrd.lrd_xid); + printf(" last_result: %u\n", lrd.lrd_result); + printf(" last_data: %llu\n\n", lrd.lrd_data); + } + } + +out_close: + if (filep != NULL) + fclose(filep); out_rmdir: - memset(cmd, 0, sizeof(cmd)); - sprintf(cmd, "rm -rf %s", tmpdir); - run_command(cmd); + rmdir(tmpdir); return ret; } -- 1.8.3.1