text: /*
   * Copyright (c) 2014-2015, Intel Corporation
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in
   *       the documentation and/or other materials provided with the
   *       distribution.
   *
   *     * Neither the name of Intel Corporation nor the names of its
   *       contributors may be used to endorse or promote products derived
   *       from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /*
   * info_blk.c -- pmempool info command source file for blk pool
   */
  #include <stdbool.h>
  #include <stdlib.h>
  #include <string.h>
  #include <err.h>
  #include <sys/param.h>
  
  #include "common.h"
  #include "output.h"
  #include "info.h"
  
  /*
   * pmempool_info_get_range -- get blocks/data chunk range
   *
   * Get range based on command line arguments and maximum value.
   * Return value:
   * 0 - range is empty
   * 1 - range is not empty
   */
  static int
  pmempool_info_get_range(struct pmem_info *pip, struct range *rangep,
  		struct range *curp, uint32_t max, off_t offset)
  {
  	/* not using range */
  	if (!pip->args.use_range) {
  		rangep->first = 0;
  		rangep->last = max;
  
  		return 1;
  	}
  
  	if (curp->first > offset + max)
  		return 0;
  
  	if (curp->first >= offset)
  		rangep->first = curp->first - offset;
  	else
  		rangep->first = 0;
  
  	if (curp->last < offset)
  		return 0;
  
  	if (curp->last <= offset + max)
  		rangep->last = curp->last - offset;
  	else
  		rangep->last = max;
  
  	return 1;
  }
  
  /*
   * info_blk_skip_block -- get action type for block/data chunk
   *
   * Return value indicating whether processing block/data chunk
   * should be skipped.
   *
   * Return values:
   *  0 - continue processing
   *  1 - skip current block
   */
  static int
  info_blk_skip_block(struct pmem_info *pip, int is_zero,
  		int is_error)
  {
  	if (pip->args.blk.skip_no_flag && !is_zero && !is_error)
  		return 1;
  
  	if (is_zero && pip->args.blk.skip_zeros)
  		return 1;
  
  	if (is_error && pip->args.blk.skip_error)
  		return 1;
  
  	return 0;
  }
  
  /*
   * info_btt_data -- print block data and corresponding flags from map
   */
  static int
  info_btt_data(struct pmem_info *pip, int v,
  	struct btt_info *infop, off_t arena_off, off_t offset, off_t *countp)
  {
  	if (!outv_check(v))
  		return 0;
  
  	int ret = 0;
  
  	size_t mapsize = infop->external_nlba * BTT_MAP_ENTRY_SIZE;
  	uint32_t *map = malloc(mapsize);
  	if (!map)
  		err(1, "Cannot allocate memory for BTT map");
  
  	uint8_t *block_buff = malloc(infop->external_lbasize);
  	if (!block_buff)
  		err(1, "Cannot allocate memory for pmemblk block buffer");
  
  	/* read btt map area */
  	if (pmempool_info_read(pip, (uint8_t *)map, mapsize,
  				arena_off + infop->mapoff)) {
  		outv_err("wrong BTT Map size or offset\n");
  		ret = -1;
  		goto error;
  	}
  
  	uint32_t i;
  	struct range *curp = NULL;
  	struct range range;
  	FOREACH_RANGE(curp, &pip->args.ranges) {
  		if (pmempool_info_get_range(pip, &range, curp,
  					infop->external_nlba - 1, offset) == 0)
  			continue;
  		for (i = range.first; i <= range.last; i++) {
  			uint32_t map_entry = le32toh(map[i]);
  			int is_init = (map_entry & ~BTT_MAP_ENTRY_LBA_MASK)
  				== 0;
  			int is_zero = (map_entry & ~BTT_MAP_ENTRY_LBA_MASK)
  				== BTT_MAP_ENTRY_ZERO || is_init;
  			int is_error = (map_entry & ~BTT_MAP_ENTRY_LBA_MASK)
  				== BTT_MAP_ENTRY_ERROR;
  
  			uint32_t blockno = is_init ? i :
  					map_entry & BTT_MAP_ENTRY_LBA_MASK;
  
  			if (info_blk_skip_block(pip,
  						is_zero, is_error))
  				continue;
  
  			/* compute block's data address */
  			off_t block_off = arena_off + infop->dataoff +
  				blockno * infop->internal_lbasize;
  
  			if (pmempool_info_read(pip, block_buff,
  					infop->external_lbasize, block_off)) {
  				outv_err("cannot read %d block\n", i);
  				ret = -1;
  				goto error;
  			}
  
  			if (*countp == 0)
  				outv_title(v, "PMEM BLK blocks data");
  
  			/*
  			 * Print block number, offset and flags
  			 * from map entry.
  			 */
  			outv(v, "Block %10d: offset: %s\n",
  				offset + i,
  				out_get_btt_map_entry(map_entry));
  
  			/* dump block's data */
  			outv_hexdump(v, block_buff, infop->external_lbasize,
  					block_off, 1);
  
  			*countp = *countp + 1;
  		}
  	}
  error:
  	free(map);
  	free(block_buff);
  	return ret;
  }
  
  /*
   * info_btt_map -- print all map entries
   */
  static int
  info_btt_map(struct pmem_info *pip, int v,
  		struct btt_info *infop, off_t arena_off,
  		off_t offset, off_t *count)
  {
  	if (!outv_check(v) && !outv_check(pip->args.vstats))
  		return 0;
  
  	int ret = 0;
  	size_t mapsize = infop->external_nlba * BTT_MAP_ENTRY_SIZE;
  
  	uint32_t *map = malloc(mapsize);
  	if (!map)
  		err(1, "Cannot allocate memory for BTT map");
  
  	/* read btt map area */
  	if (pmempool_info_read(pip, (uint8_t *)map, mapsize,
  				arena_off + infop->mapoff)) {
  		outv_err("wrong BTT Map size or offset\n");
  		ret = -1;
  		goto error;
  	}
  
  	uint32_t arena_count = 0;
  
  	int i;
  	struct range *curp = NULL;
  	struct range range;
  	FOREACH_RANGE(curp, &pip->args.ranges) {
  		if (pmempool_info_get_range(pip, &range, curp,
  					infop->external_nlba - 1, offset) == 0)
  			continue;
  		for (i = range.first; i <= range.last; i++) {
  			uint32_t entry = le32toh(map[i]);
  			int is_zero = (entry & ~BTT_MAP_ENTRY_LBA_MASK) ==
  				BTT_MAP_ENTRY_ZERO ||
  				(entry & ~BTT_MAP_ENTRY_LBA_MASK) == 0;
  			int is_error = (entry & ~BTT_MAP_ENTRY_LBA_MASK) ==
  				BTT_MAP_ENTRY_ERROR;
  
  			if (info_blk_skip_block(pip,
  					is_zero, is_error) == 0) {
  				if (arena_count == 0)
  					outv_title(v, "PMEM BLK BTT Map");
  
  				if (is_zero)
  					pip->blk.stats.zeros++;
  				if (is_error)
  					pip->blk.stats.errors++;
  				if (!is_zero && !is_error)
  					pip->blk.stats.noflag++;
  
  				pip->blk.stats.total++;
  
  				arena_count++;
  				(*count)++;
  
  				outv(v, "%010d: %s\n", offset + i,
  					out_get_btt_map_entry(entry));
  			}
  		}
  	}
  error:
  	free(map);
  	return ret;
  }
  
  /*
   * info_btt_flog_convert -- convert flog entry
   */
  static void
  info_btt_flog_convert(struct btt_flog *flogp)
  {
  	flogp->lba = le32toh(flogp->lba);
  	flogp->old_map = le32toh(flogp->old_map);
  	flogp->new_map = le32toh(flogp->new_map);
  	flogp->seq = le32toh(flogp->seq);
  }
  
  /*
   * info_btt_flog -- print all flog entries
   */
  static int
  info_btt_flog(struct pmem_info *pip, int v,
  		struct btt_info *infop, off_t arena_off)
  {
  	if (!outv_check(v))
  		return 0;
  
  	int ret = 0;
  	struct btt_flog *flogp = NULL;
  	struct btt_flog *flogpp = NULL;
  	uint64_t flog_size = infop->nfree *
  		roundup(2 * sizeof (struct btt_flog), BTT_FLOG_PAIR_ALIGN);
  	flog_size = roundup(flog_size, BTT_ALIGNMENT);
  	uint8_t *buff = malloc(flog_size);
  	if (!buff)
  		err(1, "Cannot allocate memory for FLOG entries");
  
  	if (pmempool_info_read(pip, buff, flog_size,
  				arena_off + infop->flogoff)) {
  		outv_err("cannot read BTT FLOG");
  		ret = -1;
  		goto error;
  	}
  
  	outv_title(v, "PMEM BLK BTT FLOG");
  
  	uint8_t *ptr = buff;
  	int i;
  	for (i = 0; i < infop->nfree; i++) {
  		flogp = (struct btt_flog *)ptr;
  		flogpp = flogp + 1;
  
  		info_btt_flog_convert(flogp);
  		info_btt_flog_convert(flogpp);
  
  		outv(v, "%010d:\n", i);
  		outv_field(v, "LBA", "0x%08x", flogp->lba);
  		outv_field(v, "Old map", "0x%08x: %s", flogp->old_map,
  			out_get_btt_map_entry(flogp->old_map));
  		outv_field(v, "New map", "0x%08x: %s", flogp->new_map,
  			out_get_btt_map_entry(flogp->new_map));
  		outv_field(v, "Seq", "0x%x", flogp->seq);
  
  		outv_field(v, "LBA'", "0x%08x", flogpp->lba);
  		outv_field(v, "Old map'", "0x%08x: %s", flogpp->old_map,
  			out_get_btt_map_entry(flogpp->old_map));
  		outv_field(v, "New map'", "0x%08x: %s", flogpp->new_map,
  			out_get_btt_map_entry(flogpp->new_map));
  		outv_field(v, "Seq'", "0x%x", flogpp->seq);
  
  		ptr += BTT_FLOG_PAIR_ALIGN;
  	}
  error:
  	free(buff);
  	return ret;
  }
  
  /*
   * info_btt_stats -- print btt related statistics
   */
  static void
  info_btt_stats(struct pmem_info *pip, int v)
  {
  	if (pip->blk.stats.total > 0) {
  		outv_title(v, "PMEM BLK Statistics");
  		double perc_zeros = (double)pip->blk.stats.zeros /
  			(double)pip->blk.stats.total * 100.0;
  		double perc_errors = (double)pip->blk.stats.errors /
  			(double)pip->blk.stats.total * 100.0;
  		double perc_noflag = (double)pip->blk.stats.noflag /
  			(double)pip->blk.stats.total * 100.0;
  
  		outv_field(v, "Total blocks", "%u", pip->blk.stats.total);
  		outv_field(v, "Zeroed blocks", "%u [%s]", pip->blk.stats.zeros,
  				out_get_percentage(perc_zeros));
  		outv_field(v, "Error blocks", "%u [%s]", pip->blk.stats.errors,
  				out_get_percentage(perc_errors));
  		outv_field(v, "Blocks without flag", "%u [%s]",
  				pip->blk.stats.noflag,
  				out_get_percentage(perc_noflag));
  	}
  }
  
  /*
   * info_btt_info -- print btt_info structure fields
   */
  static int
  info_btt_info(struct pmem_info *pip, int v, struct btt_info *infop)
  {
  	outv_field(v, "Signature", "%.*s", BTTINFO_SIG_LEN, infop->sig);
  
  	outv_field(v, "UUID of container", "%s",
  			out_get_uuid_str(infop->parent_uuid));
  
  	outv_field(v, "Flags", "0x%x", infop->flags);
  	outv_field(v, "Major", "%d", infop->major);
  	outv_field(v, "Minor", "%d", infop->minor);
  	outv_field(v, "External LBA size", "%s",
  			out_get_size_str(infop->external_lbasize,
  				pip->args.human));
  	outv_field(v, "External LBA count", "%u", infop->external_nlba);
  	outv_field(v, "Internal LBA size", "%s",
  			out_get_size_str(infop->internal_lbasize,
  				pip->args.human));
  	outv_field(v, "Internal LBA count", "%u", infop->internal_nlba);
  	outv_field(v, "Free blocks", "%u", infop->nfree);
  	outv_field(v, "Info block size", "%s",
  		out_get_size_str(infop->infosize, pip->args.human));
  	outv_field(v, "Next arena offset", "0x%lx", infop->nextoff);
  	outv_field(v, "Arena data offset", "0x%lx", infop->dataoff);
  	outv_field(v, "Area map offset", "0x%lx", infop->mapoff);
  	outv_field(v, "Area flog offset", "0x%lx", infop->flogoff);
  	outv_field(v, "Info block backup offset", "0x%lx", infop->infooff);
  	outv_field(v, "Checksum", "%s", out_get_checksum(infop,
  				sizeof (*infop), &infop->checksum));
  
  	return 0;
  }
  
  /*
   * info_btt_layout -- print information about BTT layout
   */
  static int
  info_btt_layout(struct pmem_info *pip, struct pmemblk *pbp,
  		off_t btt_off)
  {
  	int ret = 0;
  
  	if (btt_off <= 0) {
  		outv_err("wrong BTT layout offset\n");
  		return -1;
  	}
  
  	struct btt_info *infop = NULL;
  
  	infop = malloc(sizeof (struct btt_info));
  	if (!infop)
  		err(1, "Cannot allocate memory for BTT Info structure");
  
  	int narena = 0;
  	off_t cur_lba = 0;
  	off_t count_data = 0;
  	off_t count_map = 0;
  	off_t offset = btt_off;
  	off_t nextoff = 0;
  
  	do {
  		/* read btt info area */
  		if (pmempool_info_read(pip, infop, sizeof (*infop), offset)) {
  			ret = -1;
  			outv_err("cannot read BTT Info header\n");
  			goto err;
  		}
  
  		if (util_check_memory((uint8_t *)infop,
  					sizeof (*infop), 0) == 0) {
  			outv(1, "\n<No BTT layout>\n");
  			break;
  		}
  
  		outv(1, "\n[ARENA %d]", narena);
  		outv_title(1, "PMEM BLK BTT Info Header");
  		outv_hexdump(pip->args.vhdrdump, infop,
  				sizeof (*infop), offset, 1);
  
  		util_convert2h_btt_info(infop);
  
  		nextoff = infop->nextoff;
  
  		/* print btt info fields */
  		if (info_btt_info(pip, 1, infop)) {
  			ret = -1;
  			goto err;
  		}
  
  		/* dump blocks data */
  		if (info_btt_data(pip, pip->args.vdata,
  					infop, offset, cur_lba, &count_data)) {
  			ret = -1;
  			goto err;
  		}
  
  		/* print btt map entries and get statistics */
  		if (info_btt_map(pip, pip->args.blk.vmap, infop,
  					offset, cur_lba, &count_map)) {
  			ret = -1;
  			goto err;
  		}
  
  		/* print flog entries */
  		if (info_btt_flog(pip, pip->args.blk.vflog, infop,
  					offset)) {
  			ret = -1;
  			goto err;
  		}
  
  		/* increment LBA's counter before reading info backup */
  		cur_lba += infop->external_nlba;
  
  		/* read btt info backup area */
  		if (pmempool_info_read(pip, infop, sizeof (*infop),
  			offset + infop->infooff)) {
  			outv_err("wrong BTT Info Backup size or offset\n");
  			ret = -1;
  			goto err;
  		}
  
  		outv_title(pip->args.blk.vbackup,
  				"PMEM BLK BTT Info Header Backup");
  		if (outv_check(pip->args.blk.vbackup))
  			outv_hexdump(pip->args.vhdrdump, infop,
  				sizeof (*infop),
  				offset + infop->infooff, 1);
  
  		util_convert2h_btt_info(infop);
  		info_btt_info(pip, pip->args.blk.vbackup, infop);
  
  		offset += nextoff;
  		narena++;
  
  	} while (nextoff > 0);
  
  	info_btt_stats(pip, pip->args.vstats);
  
  err:
  	if (infop)
  		free(infop);
  
  	return ret;
  }
  
  /*
   * info_blk_descriptor -- print pmemblk descriptor
   */
  static void
  info_blk_descriptor(struct pmem_info *pip, int v, struct pmemblk *pbp)
  {
  	outv_title(v, "PMEM BLK Header");
  	/* dump pmemblk header without pool_hdr */
  	outv_hexdump(pip->args.vhdrdump, (uint8_t *)pbp + sizeof (pbp->hdr),
  		sizeof (*pbp) - sizeof (pbp->hdr), sizeof (pbp->hdr), 1);
  	outv_field(v, "Block size", "%s",
  			out_get_size_str(pbp->bsize, pip->args.human));
  	outv_field(v, "Is zeroed", pbp->is_zeroed ? "true" : "false");
  }
  
  /*
   * pmempool_info_blk -- print information about block type pool
   */
  int
  pmempool_info_blk(struct pmem_info *pip)
  {
  	int ret;
  	struct pmemblk *pbp = malloc(sizeof (struct pmemblk));
  	if (!pbp)
  		err(1, "Cannot allocate memory for pmemblk structure");
  
  	if (pmempool_info_read(pip, pbp, sizeof (struct pmemblk), 0)) {
  		outv_err("cannot read pmemblk header\n");
  		free(pbp);
  		return -1;
  	}
  
  	info_blk_descriptor(pip, VERBOSE_DEFAULT, pbp);
  
  	ssize_t btt_off = pbp->data - pbp->addr;
  	ret = info_btt_layout(pip, pbp, btt_off);
  
  	free(pbp);
  
  	return ret;
  }
  