kmod/libkmod/libkmod-file-zstd.c
Emil Velikov 72c0aa37fe libkmod: use bufferless zstd decompression
Unlike the other two decompressions, zstd supports streaming bufferless
mode. Meaning we don't need to read and realloc in a loop.

Some strace numbers:

        $ strace -e read ./before/depmod -o /tmp/throaway | wc -l
        35265
        $ strace -e fstat ./before/depmod -o /tmp/throaway | wc -l
        1110

        $ strace -e read ./after/depmod -o /tmp/throaway | wc -l
        5677
        $ strace -e fstat ./after/depmod -o /tmp/throaway | wc -l
        6642

.. and valgrind total heap usage statistics:
        before:
        1,039,426 allocs, 1,039,426 frees, 3,679,232,922 bytes allocated

        after:
        1,020,643 allocs, 1,020,643 frees, 1,157,922,357 bytes allocated

The actual runtime is within the error margin.

v2:
 - use ZSTD_decompress(), which allocates ZSTD_DCtx internally

Signed-off-by: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://github.com/kmod-project/kmod/pull/142
Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2024-09-23 09:53:48 -05:00

87 lines
1.6 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
/*
* Copyright © 2024 Intel Corporation
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zstd.h>
#include <shared/util.h>
#include "libkmod.h"
#include "libkmod-internal.h"
#include "libkmod-internal-file.h"
int kmod_file_load_zstd(struct kmod_file *file)
{
void *src_buf = MAP_FAILED, *dst_buf = NULL;
size_t src_size, dst_size, ret;
unsigned long long frame_size;
struct stat st;
if (fstat(file->fd, &st) < 0) {
ret = -errno;
ERR(file->ctx, "zstd: %m\n");
goto out;
}
if ((uintmax_t)st.st_size > SIZE_MAX) {
ret = -ENOMEM;
goto out;
}
src_size = st.st_size;
src_buf = mmap(NULL, src_size, PROT_READ, MAP_PRIVATE, file->fd, 0);
if (src_buf == MAP_FAILED) {
ret = -errno;
goto out;
}
frame_size = ZSTD_getFrameContentSize(src_buf, src_size);
if (frame_size == 0 || frame_size == ZSTD_CONTENTSIZE_UNKNOWN ||
frame_size == ZSTD_CONTENTSIZE_ERROR) {
ret = -EINVAL;
ERR(file->ctx, "zstd: Failed to determine decompression size\n");
goto out;
}
if (frame_size > SIZE_MAX) {
ret = -ENOMEM;
goto out;
}
dst_size = frame_size;
dst_buf = malloc(dst_size);
if (dst_buf == NULL) {
ret = -errno;
goto out;
}
ret = ZSTD_decompress(dst_buf, dst_size, src_buf, src_size);
if (ZSTD_isError(ret)) {
ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(ret));
goto out;
}
file->memory = dst_buf;
file->size = dst_size;
ret = 0;
dst_buf = NULL;
out:
free(dst_buf);
if (src_buf != MAP_FAILED)
munmap(src_buf, src_size);
return ret;
}