kmod/libkmod/libkmod-file.c

380 lines
7.6 KiB
C
Raw Normal View History

2011-12-17 01:08:53 +07:00
/*
* libkmod - interface to kernel module operations
*
2013-01-16 20:27:21 +07:00
* Copyright (C) 2011-2013 ProFUSION embedded systems
2011-12-17 01:08:53 +07:00
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdbool.h>
2011-12-17 01:08:53 +07:00
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include "libkmod.h"
#include "libkmod-internal.h"
2011-12-17 01:08:53 +07:00
#ifdef ENABLE_XZ
#include <lzma.h>
#endif
2011-12-17 01:08:53 +07:00
#ifdef ENABLE_ZLIB
#include <zlib.h>
#endif
struct kmod_file;
struct file_ops {
int (*load)(struct kmod_file *file);
void (*unload)(struct kmod_file *file);
};
2011-12-17 01:08:53 +07:00
struct kmod_file {
#ifdef ENABLE_XZ
bool xz_used;
#endif
2011-12-17 01:08:53 +07:00
#ifdef ENABLE_ZLIB
gzFile gzf;
#endif
int fd;
bool direct;
2011-12-17 01:08:53 +07:00
off_t size;
void *memory;
const struct file_ops *ops;
2012-01-04 17:19:34 +07:00
const struct kmod_ctx *ctx;
libkmod: cache open file for later access If we are accessing several times the modules and reading some sections by sucessive calls to the functions below, we are incurring in a penalty of having to open, parse the header and close the file. For each function. - kmod_module_get_info() - kmod_module_get_versions() - kmod_module_get_symbols() - kmod_module_get_dependency_symbols() These functions are particularly important to depmod. It calls all of them, for each module. Moreover there's a huge bottleneck in the open operation if we are using compression. Every time we open the module we need to uncompress the file and after getting the information we need we discard the result. This is clearly shown by profiling depmod with perf (record + report), using compressed modules: 64.07% depmod libz.so.1.2.7 [.] 0x00000000000074b8 ◆ 18.18% depmod libz.so.1.2.7 [.] crc32 ▒ 2.42% depmod libz.so.1.2.7 [.] inflate ▒ 1.17% depmod libc-2.16.so [.] __memcpy_ssse3_back ▒ 0.96% depmod [kernel.kallsyms] [k] copy_user_generic_string ▒ 0.89% depmod libc-2.16.so [.] __strcmp_sse42 ▒ 0.82% depmod [kernel.kallsyms] [k] hrtimer_interrupt ▒ 0.77% depmod libc-2.16.so [.] _int_malloc ▒ 0.44% depmod kmod-nolib [.] kmod_elf_get_strings ▒ 0.41% depmod kmod-nolib [.] kmod_elf_get_dependency_symbols ▒ 0.37% depmod kmod-nolib [.] kmod_elf_get_section ▒ 0.36% depmod kmod-nolib [.] kmod_elf_get_symbols ... Average of running depmod 5 times, dropping caches between them, in a slow spinning disk: Before: 12.25 +- 0.20 After: 8.20 +- 0.21 m-i-t: 9.62 +- 0.27 So this patch leads to an improvement of ~33% over unpatched version, ending up with 15% speedup over module-init-tools.
2012-10-18 11:36:33 +07:00
struct kmod_elf *elf;
2011-12-17 01:08:53 +07:00
};
#ifdef ENABLE_XZ
static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
{
switch (ret) {
case LZMA_MEM_ERROR:
ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
break;
case LZMA_FORMAT_ERROR:
ERR(file->ctx, "xz: File format not recognized\n");
break;
case LZMA_OPTIONS_ERROR:
ERR(file->ctx, "xz: Unsupported compression options\n");
break;
case LZMA_DATA_ERROR:
ERR(file->ctx, "xz: File is corrupt\n");
break;
case LZMA_BUF_ERROR:
ERR(file->ctx, "xz: Unexpected end of input\n");
break;
default:
ERR(file->ctx, "xz: Internal error (bug)\n");
break;
}
}
static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
{
uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
lzma_action action = LZMA_RUN;
lzma_ret ret;
void *p = NULL;
size_t total = 0;
strm->avail_in = 0;
strm->next_out = out_buf;
strm->avail_out = sizeof(out_buf);
while (true) {
if (strm->avail_in == 0) {
ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
if (rdret < 0) {
ret = -errno;
goto out;
}
strm->next_in = in_buf;
strm->avail_in = rdret;
if (rdret == 0)
action = LZMA_FINISH;
}
ret = lzma_code(strm, action);
if (strm->avail_out == 0 || ret != LZMA_OK) {
size_t write_size = BUFSIZ - strm->avail_out;
char *tmp = realloc(p, total + write_size);
if (tmp == NULL) {
ret = -errno;
goto out;
}
memcpy(tmp + total, out_buf, write_size);
total += write_size;
p = tmp;
strm->next_out = out_buf;
strm->avail_out = BUFSIZ;
}
if (ret == LZMA_STREAM_END)
break;
if (ret != LZMA_OK) {
xz_uncompress_belch(file, ret);
ret = -EINVAL;
goto out;
}
}
file->xz_used = true;
file->memory = p;
file->size = total;
return 0;
out:
free(p);
return ret;
}
static int load_xz(struct kmod_file *file)
{
lzma_stream strm = LZMA_STREAM_INIT;
lzma_ret lzret;
int ret;
lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
if (lzret == LZMA_MEM_ERROR) {
ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
return -ENOMEM;
} else if (lzret != LZMA_OK) {
ERR(file->ctx, "xz: Internal error (bug)\n");
return -EINVAL;
}
ret = xz_uncompress(&strm, file);
lzma_end(&strm);
return ret;
}
static void unload_xz(struct kmod_file *file)
{
if (!file->xz_used)
return;
free(file->memory);
}
static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
#endif
#ifdef ENABLE_ZLIB
#define READ_STEP (4 * 1024 * 1024)
static int load_zlib(struct kmod_file *file)
2011-12-17 01:08:53 +07:00
{
int err = 0;
off_t did = 0, total = 0;
unsigned char *p = NULL;
errno = 0;
file->gzf = gzdopen(file->fd, "rb");
if (file->gzf == NULL) {
2011-12-17 01:08:53 +07:00
return -errno;
}
file->fd = -1; /* now owned by gzf due gzdopen() */
2011-12-17 01:08:53 +07:00
for (;;) {
int r;
if (did == total) {
void *tmp = realloc(p, total + READ_STEP);
if (tmp == NULL) {
err = -errno;
goto error;
}
total += READ_STEP;
p = tmp;
}
r = gzread(file->gzf, p + did, total - did);
if (r == 0)
break;
else if (r < 0) {
int gzerr;
const char *gz_errmsg = gzerror(file->gzf, &gzerr);
ERR(file->ctx, "gzip: %s\n", gz_errmsg);
/* gzip might not set errno here */
err = gzerr == Z_ERRNO ? -errno : -EINVAL;
2011-12-17 01:08:53 +07:00
goto error;
}
did += r;
}
file->memory = p;
file->size = did;
return 0;
error:
free(p);
gzclose(file->gzf);
return err;
}
static void unload_zlib(struct kmod_file *file)
{
if (file->gzf == NULL)
return;
free(file->memory);
gzclose(file->gzf); /* closes file->fd */
}
static const char magic_zlib[] = {0x1f, 0x8b};
#endif
static const struct comp_type {
size_t magic_size;
const char *magic_bytes;
const struct file_ops ops;
} comp_types[] = {
#ifdef ENABLE_XZ
{sizeof(magic_xz), magic_xz, {load_xz, unload_xz}},
#endif
#ifdef ENABLE_ZLIB
{sizeof(magic_zlib), magic_zlib, {load_zlib, unload_zlib}},
#endif
{0, NULL, {NULL, NULL}}
};
static int load_reg(struct kmod_file *file)
2011-12-17 01:08:53 +07:00
{
struct stat st;
if (fstat(file->fd, &st) < 0)
return -errno;
2011-12-17 01:08:53 +07:00
file->size = st.st_size;
file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
file->fd, 0);
if (file->memory == MAP_FAILED)
return -errno;
file->direct = true;
2011-12-17 01:08:53 +07:00
return 0;
}
static void unload_reg(struct kmod_file *file)
{
munmap(file->memory, file->size);
}
2012-01-04 17:00:45 +07:00
static const struct file_ops reg_ops = {
load_reg, unload_reg
};
libkmod: cache open file for later access If we are accessing several times the modules and reading some sections by sucessive calls to the functions below, we are incurring in a penalty of having to open, parse the header and close the file. For each function. - kmod_module_get_info() - kmod_module_get_versions() - kmod_module_get_symbols() - kmod_module_get_dependency_symbols() These functions are particularly important to depmod. It calls all of them, for each module. Moreover there's a huge bottleneck in the open operation if we are using compression. Every time we open the module we need to uncompress the file and after getting the information we need we discard the result. This is clearly shown by profiling depmod with perf (record + report), using compressed modules: 64.07% depmod libz.so.1.2.7 [.] 0x00000000000074b8 ◆ 18.18% depmod libz.so.1.2.7 [.] crc32 ▒ 2.42% depmod libz.so.1.2.7 [.] inflate ▒ 1.17% depmod libc-2.16.so [.] __memcpy_ssse3_back ▒ 0.96% depmod [kernel.kallsyms] [k] copy_user_generic_string ▒ 0.89% depmod libc-2.16.so [.] __strcmp_sse42 ▒ 0.82% depmod [kernel.kallsyms] [k] hrtimer_interrupt ▒ 0.77% depmod libc-2.16.so [.] _int_malloc ▒ 0.44% depmod kmod-nolib [.] kmod_elf_get_strings ▒ 0.41% depmod kmod-nolib [.] kmod_elf_get_dependency_symbols ▒ 0.37% depmod kmod-nolib [.] kmod_elf_get_section ▒ 0.36% depmod kmod-nolib [.] kmod_elf_get_symbols ... Average of running depmod 5 times, dropping caches between them, in a slow spinning disk: Before: 12.25 +- 0.20 After: 8.20 +- 0.21 m-i-t: 9.62 +- 0.27 So this patch leads to an improvement of ~33% over unpatched version, ending up with 15% speedup over module-init-tools.
2012-10-18 11:36:33 +07:00
struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
{
if (file->elf)
return file->elf;
file->elf = kmod_elf_new(file->memory, file->size);
return file->elf;
}
2012-01-04 17:19:34 +07:00
struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
const char *filename)
2011-12-17 01:08:53 +07:00
{
struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
const struct comp_type *itr;
size_t magic_size_max = 0;
2011-12-17 01:08:53 +07:00
int err;
if (file == NULL)
return NULL;
file->fd = open(filename, O_RDONLY|O_CLOEXEC);
if (file->fd < 0) {
err = -errno;
goto error;
}
for (itr = comp_types; itr->ops.load != NULL; itr++) {
if (magic_size_max < itr->magic_size)
magic_size_max = itr->magic_size;
}
file->direct = false;
if (magic_size_max > 0) {
char *buf = alloca(magic_size_max + 1);
ssize_t sz;
2011-12-17 01:08:53 +07:00
if (buf == NULL) {
err = -errno;
goto error;
}
sz = read_str_safe(file->fd, buf, magic_size_max + 1);
lseek(file->fd, 0, SEEK_SET);
if (sz != (ssize_t)magic_size_max) {
if (sz < 0)
err = sz;
else
err = -EINVAL;
goto error;
}
for (itr = comp_types; itr->ops.load != NULL; itr++) {
if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0)
break;
}
if (itr->ops.load != NULL)
file->ops = &itr->ops;
}
if (file->ops == NULL)
file->ops = &reg_ops;
err = file->ops->load(file);
2012-01-04 17:19:34 +07:00
file->ctx = ctx;
error:
2011-12-17 01:08:53 +07:00
if (err < 0) {
if (file->fd >= 0)
close(file->fd);
2011-12-17 01:08:53 +07:00
free(file);
errno = -err;
return NULL;
}
return file;
}
void *kmod_file_get_contents(const struct kmod_file *file)
{
return file->memory;
}
off_t kmod_file_get_size(const struct kmod_file *file)
{
return file->size;
}
bool kmod_file_get_direct(const struct kmod_file *file)
{
return file->direct;
}
int kmod_file_get_fd(const struct kmod_file *file)
{
return file->fd;
}
2011-12-17 01:08:53 +07:00
void kmod_file_unref(struct kmod_file *file)
{
libkmod: cache open file for later access If we are accessing several times the modules and reading some sections by sucessive calls to the functions below, we are incurring in a penalty of having to open, parse the header and close the file. For each function. - kmod_module_get_info() - kmod_module_get_versions() - kmod_module_get_symbols() - kmod_module_get_dependency_symbols() These functions are particularly important to depmod. It calls all of them, for each module. Moreover there's a huge bottleneck in the open operation if we are using compression. Every time we open the module we need to uncompress the file and after getting the information we need we discard the result. This is clearly shown by profiling depmod with perf (record + report), using compressed modules: 64.07% depmod libz.so.1.2.7 [.] 0x00000000000074b8 ◆ 18.18% depmod libz.so.1.2.7 [.] crc32 ▒ 2.42% depmod libz.so.1.2.7 [.] inflate ▒ 1.17% depmod libc-2.16.so [.] __memcpy_ssse3_back ▒ 0.96% depmod [kernel.kallsyms] [k] copy_user_generic_string ▒ 0.89% depmod libc-2.16.so [.] __strcmp_sse42 ▒ 0.82% depmod [kernel.kallsyms] [k] hrtimer_interrupt ▒ 0.77% depmod libc-2.16.so [.] _int_malloc ▒ 0.44% depmod kmod-nolib [.] kmod_elf_get_strings ▒ 0.41% depmod kmod-nolib [.] kmod_elf_get_dependency_symbols ▒ 0.37% depmod kmod-nolib [.] kmod_elf_get_section ▒ 0.36% depmod kmod-nolib [.] kmod_elf_get_symbols ... Average of running depmod 5 times, dropping caches between them, in a slow spinning disk: Before: 12.25 +- 0.20 After: 8.20 +- 0.21 m-i-t: 9.62 +- 0.27 So this patch leads to an improvement of ~33% over unpatched version, ending up with 15% speedup over module-init-tools.
2012-10-18 11:36:33 +07:00
if (file->elf)
kmod_elf_unref(file->elf);
file->ops->unload(file);
if (file->fd >= 0)
close(file->fd);
2011-12-17 01:08:53 +07:00
free(file);
}