zdump/dfi_s390: Support reading compressed s390_ext dumps

Update dfi_s390.c to support reading of compressed dump segments.
For this, introduce a callback function for reading memory chunks
associated with compressed dump segments. Apart from the segment location
on disk this function requires the entry_offset array from the dump segment
header in order to process each compressed entry separately, thus allowing
fast seek processing for zgetdump (no need to decompress a big dump segment
to extract a single piece of data).

In addition, split mem_chunks_add_ext() in several functions.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
Mikhail Zaslonko
2023-01-05 15:06:30 +01:00
committed by Jan Höppner
parent 2363269c1c
commit 271b809495

View File

@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <zlib.h>
#include "lib/util_log.h"
#include "dump/s390_dump.h"
@@ -36,8 +37,21 @@ static struct {
struct df_s390_hdr hdr; /* s390 dump header */
struct df_s390_em em; /* s390 end marker */
bool extended; /* Extended input dump format */
int blk_size; /* Dump device block size */
} l;
struct mem_chunk_compressed_data {
/* Offset of the dump segment data on disk in bytes */
u64 offset_on_disk;
/* Number of compressed entries */
u32 entry_count;
/*
* Offsets to compressed entries from
* the start of mem_chunk in blocks
*/
u32 entry_offset[];
};
/*
* s390 mem chunk read callback
*/
@@ -57,11 +71,118 @@ static void dfi_s390_ext_mem_chunk_read(struct dfi_mem_chunk *mem_chunk,
u64 off, void *buf, u64 cnt)
{
u64 *mem_chunk_off = mem_chunk->data;
util_log_print(UTIL_LOG_DEBUG,
"DFI S390 mem_chunk_read: start=0x%016lx, off=0x%016lx, cnt=0x%08lx\n",
mem_chunk->start, off, cnt);
zg_seek(g.fh, *mem_chunk_off + off, ZG_CHECK);
zg_read(g.fh, buf, cnt, ZG_CHECK);
}
static inline unsigned long b2m(unsigned long blk)
{
return blk * l.blk_size;
}
/*
* Read compressed data entry using global file handle and decompress up to
* count bytes to the output buffer. The size of the output buffer considered
* to be enough to fit the decompressed data.
*/
static unsigned long read_decompress_entry(void *buf_out, u64 count)
{
unsigned char buf_in[8 * PAGE_SIZE];
z_stream strm = { 0 };
u64 total_out;
int rc;
/* Initialize inflate stream */
strm.next_in = NULL;
strm.avail_in = 0;
rc = inflateInit2(&strm, MAX_WBITS);
if (rc != Z_OK)
ERR_EXIT("Decompression failed, inflateInit RC = %d", rc);
strm.next_out = buf_out;
strm.avail_out = count;
/* Decompress data entry to the output buffer */
while (rc != Z_STREAM_END) {
/* Read in more compressed data */
if (strm.avail_in == 0) {
strm.next_in = buf_in;
rc = zg_read(g.fh, buf_in, sizeof(buf_in), ZG_CHECK_NONE);
if (rc < 0)
ERR_EXIT("Decompression failed, read error encountered");
strm.avail_in = rc;
}
rc = inflate(&strm, Z_SYNC_FLUSH);
if (rc != Z_OK && rc != Z_STREAM_END)
ERR_EXIT("Decompression failed, inflate RC = %d", rc);
/* No need to decompress more data */
if (strm.avail_out == 0)
break;
}
total_out = strm.total_out;
inflateEnd(&strm);
return total_out;
}
/*
* s390_ext compressed mem chunk read callback
*/
static void dfi_s390_ext_mem_chunk_read_decompress(struct dfi_mem_chunk *mem_chunk,
u64 off, void *buf, u64 cnt)
{
u64 bytes_to_copy, start_offset, compressed_data_addr, copied, decompressed_out;
struct mem_chunk_compressed_data *data = mem_chunk->data;
u32 uncompressed, entry_size, entry_index;
void *buf_out;
util_log_print(UTIL_LOG_DEBUG,
"DFI S390 mem_chunk_read_decomp: start=0x%016lx, offset_on_disk=0x%016lx, off=0x%016lx, cnt=0x%08lx\n",
mem_chunk->start, data->offset_on_disk, off, cnt);
/*
* The dump is compressed in data pieces of equal size (stored in the dump header).
* Identify the first compressed entry of interest within a memory chunk.
*/
entry_size = l.hdr.zlib_entry_size;
entry_index = off / entry_size;
/* Offset within the decompressed data of the first entry */
start_offset = off % entry_size;
buf_out = zg_alloc(entry_size);
copied = 0;
while (copied < cnt) {
if (entry_index >= data->entry_count)
ERR_EXIT("Decompression failed, compressed entry index out of bounds");
/* Check if the entry is actually compressed */
uncompressed = data->entry_offset[entry_index] & DUMP_SEGM_ENTRY_UNCOMPRESSED;
/* Calculate entry location on disk */
compressed_data_addr = data->offset_on_disk +
b2m(~DUMP_SEGM_ENTRY_UNCOMPRESSED & data->entry_offset[entry_index]);
if (uncompressed) {
/* Read entire uncompressed entry from disk */
zg_seek(g.fh, compressed_data_addr + start_offset, ZG_CHECK);
bytes_to_copy = MIN(cnt - copied, entry_size - start_offset);
zg_read(g.fh, buf + copied, bytes_to_copy, ZG_CHECK);
} else {
/*
* Decompress entry to the output buffer. Limit the size of the
* output data if needed.
*/
zg_seek(g.fh, compressed_data_addr, ZG_CHECK);
decompressed_out = MIN(start_offset + cnt - copied, entry_size);
decompressed_out = read_decompress_entry(buf_out, decompressed_out);
/* Copy the decompressed output produced so far */
bytes_to_copy = MIN(decompressed_out - start_offset, cnt - copied);
memcpy(buf + copied, buf_out + start_offset, bytes_to_copy);
}
copied += bytes_to_copy;
entry_index++;
/* For further entries, copy from the start of the decompressed data */
start_offset = 0;
}
zg_free(buf_out);
}
/*
* Read s390 dump header
@@ -79,6 +200,8 @@ static int read_s390_hdr(void)
return -ENODEV;
if (l.hdr.cpu_cnt > DF_S390_CPU_MAX)
return -ENODEV;
if (l.hdr.zlib_version_s390 && l.hdr.version != 2)
return -ENODEV;
util_log_print(UTIL_LOG_INFO, "DFI S390 version %u\n", l.hdr.version);
df_s390_hdr_add(&l.hdr);
return 0;
@@ -122,6 +245,44 @@ static int mem_chunks_add(void)
return read_s390_em();
}
/*
* Add memory chunk for a compressed dump segment. Set file handle position
* and return the offset to the next dump segment.
*/
static void create_compressed_mem_chunk(const struct df_s390_dump_segm_hdr *dump_segm,
u64 offset)
{
/*
* Need to preserve entry_offset array from the header of a
* compressed segment along with the segment offset on disk
* for the read callback function.
*/
struct mem_chunk_compressed_data *data = zg_alloc(sizeof(*data) +
dump_segm->entry_count * sizeof(u32));
data->offset_on_disk = offset;
data->entry_count = dump_segm->entry_count;
memcpy(&data->entry_offset, &dump_segm->entry_offset,
data->entry_count * sizeof(u32));
dfi_mem_chunk_add(dump_segm->start, dump_segm->len, data,
dfi_s390_ext_mem_chunk_read_decompress, zg_free);
data = NULL;
}
/*
* Add memory chunk for a non-compressed dump segment. Set file handle position
* and return the offset to the next dump segment.
*/
static void create_uncompressed_mem_chunk(const struct df_s390_dump_segm_hdr *dump_segm,
u64 offset)
{
/* For non-compressed segment just an offset is needed for a callback */
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = offset;
dfi_mem_chunk_add(dump_segm->start, dump_segm->len, off_ptr,
dfi_s390_ext_mem_chunk_read, zg_free);
off_ptr = NULL;
}
/*
* Register memory chunks (extended dump format) and verify the end marker
*/
@@ -133,26 +294,30 @@ static int mem_chunks_add_ext(void)
off = zg_seek(g.fh, DF_S390_HDR_SIZE, ZG_CHECK_NONE);
if (off != DF_S390_HDR_SIZE)
return -EINVAL;
while (off < DF_S390_HDR_SIZE + l.hdr.mem_size - PAGE_SIZE) {
rc = zg_read(g.fh, &dump_segm, PAGE_SIZE, ZG_CHECK_ERR);
if (rc != PAGE_SIZE)
while (off < DF_S390_HDR_SIZE + l.hdr.mem_size) {
rc = zg_read(g.fh, &dump_segm, sizeof(dump_segm), ZG_CHECK_ERR);
if (rc != sizeof(dump_segm))
return -EINVAL;
util_log_print(UTIL_LOG_DEBUG,
"DFI S390 dump segment start 0x%016lx size 0x%016lx stop marker %d\n",
dump_segm.start, dump_segm.len, dump_segm.stop_marker);
off += PAGE_SIZE;
"DFI S390 dump segment start 0x%016lx size 0x%016lx compr_size 0x%08lx stop marker %d\n",
dump_segm.start, dump_segm.len, b2m(dump_segm.size_on_disk),
dump_segm.stop_marker ? 1 : 0);
off += sizeof(dump_segm);
/* Add zero memory chunk */
dfi_mem_chunk_add(old, dump_segm.start - old, NULL,
dfi_mem_chunk_read_zero, NULL);
/* Add memory chunk for a dump segment */
u64 *off_ptr = zg_alloc(sizeof(*off_ptr));
*off_ptr = off;
dfi_mem_chunk_add(dump_segm.start, dump_segm.len, off_ptr,
dfi_s390_ext_mem_chunk_read, zg_free);
off_ptr = NULL;
if (dump_segm.size_on_disk) {
/* Compressed dump segment detected */
create_compressed_mem_chunk(&dump_segm, off);
off = zg_seek_cur(g.fh, b2m(dump_segm.size_on_disk), ZG_CHECK_NONE);
dump_size += b2m(dump_segm.size_on_disk);
} else {
create_uncompressed_mem_chunk(&dump_segm, off);
off = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
dump_size += dump_segm.len;
}
old = dump_segm.start + dump_segm.len;
dump_size += dump_segm.len;
off = zg_seek_cur(g.fh, dump_segm.len, ZG_CHECK_NONE);
if (dump_segm.stop_marker)
break;
}
@@ -181,6 +346,7 @@ int dfi_s390_init_gen(bool extended)
l.extended = extended;
if (read_s390_hdr() != 0)
return -ENODEV;
zg_ioctl(g.fh, BLKSSZGET, &l.blk_size, "BLKSSZGET", ZG_CHECK);
if (!extended)
rc = mem_chunks_add();
else