mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
committed by
Jan Höppner
parent
2363269c1c
commit
271b809495
192
zdump/dfi_s390.c
192
zdump/dfi_s390.c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user