kmod/libkmod/libkmod-file.c

184 lines
4.1 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, see <http://www.gnu.org/licenses/>.
2011-12-17 01:08:53 +07:00
*/
#include <errno.h>
#include <stdbool.h>
2011-12-17 01:08:53 +07:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
2011-12-17 01:08:53 +07:00
#include <unistd.h>
#include <shared/util.h>
#include "libkmod.h"
#include "libkmod-internal.h"
#include "libkmod-internal-file.h"
2011-12-17 01:08:53 +07:00
static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
static const char magic_zlib[] = {0x1f, 0x8b};
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) {
file->memory = NULL;
return -errno;
}
2011-12-17 01:08:53 +07:00
return 0;
}
static const struct comp_type {
size_t magic_size;
enum kmod_file_compression_type compression;
const char *magic_bytes;
int (*load)(struct kmod_file *file);
} comp_types[] = {
{sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, kmod_file_load_zstd},
{sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, kmod_file_load_xz},
{sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, kmod_file_load_zlib},
{0, KMOD_FILE_COMPRESSION_NONE, NULL, load_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)
{
int err;
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)
return file->elf;
err = kmod_file_load_contents(file);
if (err) {
errno = err;
return NULL;
}
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
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;
char buf[7];
ssize_t sz;
assert_cc(sizeof(magic_zstd) < sizeof(buf));
assert_cc(sizeof(magic_xz) < sizeof(buf));
assert_cc(sizeof(magic_zlib) < sizeof(buf));
2011-12-17 01:08:53 +07:00
file = calloc(1, sizeof(struct kmod_file));
2011-12-17 01:08:53 +07:00
if (file == NULL)
return NULL;
file->fd = open(filename, O_RDONLY|O_CLOEXEC);
if (file->fd < 0) {
free(file);
return NULL;
}
sz = read_str_safe(file->fd, buf, sizeof(buf));
lseek(file->fd, 0, SEEK_SET);
if (sz != (sizeof(buf) - 1)) {
if (sz < 0)
errno = -sz;
else
errno = EINVAL;
close(file->fd);
free(file);
return NULL;
}
for (unsigned int i = 0; i < ARRAY_SIZE(comp_types); i++) {
const struct comp_type *itr = &comp_types[i];
file->load = itr->load;
file->compression = itr->compression;
if (itr->magic_size &&
memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
break;
}
}
2012-01-04 17:19:34 +07:00
file->ctx = ctx;
2011-12-17 01:08:53 +07:00
return file;
}
/*
* Callers should just check file->memory got updated
*/
int kmod_file_load_contents(struct kmod_file *file)
{
if (file->memory)
return 0;
/* The load functions already log possible errors. */
return file->load(file);
}
2011-12-17 01:08:53 +07:00
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;
}
libkmod: Use kernel decompression when available With the recent changes to bypass loading the file it's possible to reduce the work in userspace and delegating it to the kernel. Without any compression to illustrate: Before: read(3, "\177ELF\2\1", 6) = 6 lseek(3, 0, SEEK_SET) = 0 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=238592, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 238592, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd85cbd1000 finit_module(3, "", 0) = 0 munmap(0x7fd85cbd1000, 238592) = 0 close(3) = 0 After: read(3, "\177ELF\2\1", 6) = 6 lseek(3, 0, SEEK_SET) = 0 finit_module(3, "", 0) = 0 close(3) = 0 When using kernel compression now it's also possible to direct libkmod to take the finit_module() path, avoiding the decompression in userspace and just delegating it to the kernel. Before: read(3, "(\265/\375\244\0", 6) = 6 lseek(3, 0, SEEK_SET) = 0 read(3, "(\265/\375\244", 5) = 5 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa431e000 read(3, "\0\244\3\0\\y\6", 7) = 7 mmap(NULL, 372736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa414f000 brk(0x55944c6a1000) = 0x55944c6a1000 read(3, "\356|\6G\27U\20 \312\260s\211\335\333\263\326\330\336\273O\211\356\306K\360Z\341\374U6\342\221"..., 53038) = 53038 mremap(0x7f3fa431e000, 135168, 266240, MREMAP_MAYMOVE) = 0x7f3fa410e000 read(3, ",;\3\nqf\311\362\325\211\7\341\375A\355\221\371L\\\5\7\375 \32\246<(\258=K\304"..., 20851) = 20851 mremap(0x7f3fa410e000, 266240, 397312, MREMAP_MAYMOVE) = 0x7f3fa40ad000 read(3, ")\36\250\213", 4) = 4 read(3, "", 4) = 0 munmap(0x7f3fa414f000, 372736) = 0 init_module(0x7f3fa40ad010, 238592, "") = 0 munmap(0x7f3fa40ad000, 397312) = 0 close(3) = 0 After: read(3, "(\265/\375\244P", 6) = 6 lseek(3, 0, SEEK_SET) = 0 finit_module(3, "", 0x4 /* MODULE_INIT_??? */) = 0 close(3) = 0 Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2023-06-02 05:40:01 +07:00
enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
{
libkmod: Use kernel decompression when available With the recent changes to bypass loading the file it's possible to reduce the work in userspace and delegating it to the kernel. Without any compression to illustrate: Before: read(3, "\177ELF\2\1", 6) = 6 lseek(3, 0, SEEK_SET) = 0 newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=238592, ...}, AT_EMPTY_PATH) = 0 mmap(NULL, 238592, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd85cbd1000 finit_module(3, "", 0) = 0 munmap(0x7fd85cbd1000, 238592) = 0 close(3) = 0 After: read(3, "\177ELF\2\1", 6) = 6 lseek(3, 0, SEEK_SET) = 0 finit_module(3, "", 0) = 0 close(3) = 0 When using kernel compression now it's also possible to direct libkmod to take the finit_module() path, avoiding the decompression in userspace and just delegating it to the kernel. Before: read(3, "(\265/\375\244\0", 6) = 6 lseek(3, 0, SEEK_SET) = 0 read(3, "(\265/\375\244", 5) = 5 mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa431e000 read(3, "\0\244\3\0\\y\6", 7) = 7 mmap(NULL, 372736, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3fa414f000 brk(0x55944c6a1000) = 0x55944c6a1000 read(3, "\356|\6G\27U\20 \312\260s\211\335\333\263\326\330\336\273O\211\356\306K\360Z\341\374U6\342\221"..., 53038) = 53038 mremap(0x7f3fa431e000, 135168, 266240, MREMAP_MAYMOVE) = 0x7f3fa410e000 read(3, ",;\3\nqf\311\362\325\211\7\341\375A\355\221\371L\\\5\7\375 \32\246<(\258=K\304"..., 20851) = 20851 mremap(0x7f3fa410e000, 266240, 397312, MREMAP_MAYMOVE) = 0x7f3fa40ad000 read(3, ")\36\250\213", 4) = 4 read(3, "", 4) = 0 munmap(0x7f3fa414f000, 372736) = 0 init_module(0x7f3fa40ad010, 238592, "") = 0 munmap(0x7f3fa40ad000, 397312) = 0 close(3) = 0 After: read(3, "(\265/\375\244P", 6) = 6 lseek(3, 0, SEEK_SET) = 0 finit_module(3, "", 0x4 /* MODULE_INIT_??? */) = 0 close(3) = 0 Signed-off-by: Lucas De Marchi <lucas.de.marchi@gmail.com>
2023-06-02 05:40:01 +07:00
return file->compression;
}
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);
if (file->compression == KMOD_FILE_COMPRESSION_NONE) {
if (file->memory)
munmap(file->memory, file->size);
} else {
free(file->memory);
}
close(file->fd);
2011-12-17 01:08:53 +07:00
free(file);
}