/*
* Copyright 2014-2018, Intel Corporation
* Copyright (c) 2016, Microsoft Corporation. All rights reserved.
*
* 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 the copyright holder 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 <endian.h>
#include "os.h"
#include "common.h"
#include "output.h"
#include "info.h"
#include "btt.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, uint64_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,
uint64_t arena_off, uint64_t offset, uint64_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;
}
uint64_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;
uint64_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 */
uint64_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 %lu 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 %10lu: 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, uint64_t arena_off,
uint64_t offset, uint64_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;
uint64_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 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, "%010lu: %s\n", offset + i,
out_get_btt_map_entry(entry));
}
}
}
error:
free(map);
return ret;
}
/*
* info_btt_flog -- print all flog entries
*/
static int
info_btt_flog(struct pmem_info *pip, int v,
struct btt_info *infop, uint64_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;
uint32_t i;
for (i = 0; i < infop->nfree; i++) {
flogp = (struct btt_flog *)ptr;
flogpp = flogp + 1;
btt_flog_convert2h(flogp);
btt_flog_convert2h(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, 0));
return 0;
}
/*
* info_btt_layout -- print information about BTT layout
*/
static int
info_btt_layout(struct pmem_info *pip, os_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;
uint64_t cur_lba = 0;
uint64_t count_data = 0;
uint64_t count_map = 0;
uint64_t offset = (uint64_t)btt_off;
uint64_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);
btt_info_convert2h(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);
btt_info_convert2h(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)
{
size_t pmemblk_size;
#ifdef DEBUG
pmemblk_size = offsetof(struct pmemblk, write_lock);
#else
pmemblk_size = sizeof(*pbp);
#endif
outv_title(v, "PMEM BLK Header");
/* dump pmemblk header without pool_hdr */
outv_hexdump(pip->args.vhdrdump, (uint8_t *)pbp + sizeof(pbp->hdr),
pmemblk_size - 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 = (char *)pbp->data - (char *)pbp->addr;
ret = info_btt_layout(pip, btt_off);
free(pbp);
return ret;
}
/*
* pmempool_info_btt -- print information about btt device
*/
int
pmempool_info_btt(struct pmem_info *pip)
{
int ret;
outv(1, "\nBTT Device");
ret = info_btt_layout(pip, DEFAULT_HDR_SIZE);
return ret;
}