From patchwork Tue Jun 18 11:37:21 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Tokarev X-Patchwork-Id: 252241 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 5A87A2C00A0 for ; Tue, 18 Jun 2013 21:37:49 +1000 (EST) Received: from localhost ([::1]:56452 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UouEN-0004jy-9D for incoming@patchwork.ozlabs.org; Tue, 18 Jun 2013 07:37:47 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:52683) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UouE4-0004dz-DX for qemu-devel@nongnu.org; Tue, 18 Jun 2013 07:37:33 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UouDz-0008Nx-Px for qemu-devel@nongnu.org; Tue, 18 Jun 2013 07:37:28 -0400 Received: from isrv.corpit.ru ([86.62.121.231]:42127) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UouDz-0008Nr-Cn for qemu-devel@nongnu.org; Tue, 18 Jun 2013 07:37:23 -0400 Received: from [192.168.88.2] (mjt.vpn.tls.msk.ru [192.168.177.99]) by isrv.corpit.ru (Postfix) with ESMTP id 1AE3341A10; Tue, 18 Jun 2013 15:37:22 +0400 (MSK) Message-ID: <51C04671.8090305@msgid.tls.msk.ru> Date: Tue, 18 Jun 2013 15:37:21 +0400 From: Michael Tokarev Organization: Telecom Service, JSC User-Agent: Mozilla/5.0 (X11; Linux i686 on x86_64; rv:17.0) Gecko/17.0 Icedove/17.0 MIME-Version: 1.0 To: qemu-devel X-Enigmail-Version: 1.5.1 OpenPGP: id=804465C5 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 86.62.121.231 Subject: [Qemu-devel] RFC: DSO (dynamic shared objects) support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Hello. I looked at what's needed to support DSO (dynamic shared objects) in qemu, in order to be able to split functionality into loadable "plugins". It isn't exactly difficult, but a few steps are needed still. The whole thing is about splitting the functionality into plugins from one, single, qemu build, for now, not about allowing 3rd-party plugins to be distributed outside of qemu. The plan is to keep the plugins in a single directory (or several subdirectories, by type of plugins - for block, machine and other), and load them during startup. First of all, we need a global config switch/check for this kind of service. On a POSIX system this should make the executable to be linked with -rdynamic option, in order to make all symbols in the executable to be available to plugins. This is an interesting case. We may end up in some symbols from qemu libraries not linked in when building the executable, but that symbol may be used in some plugin. Such situations, if will happen, will need to be deal with on a case-by-case basis somehow. Next, the executable should be linked with -ldl (again, for a POSIX system), to be able to dynamically load anything. This goes on the same line as -rdynamic -- once we enable plugins, both linker options are enabled. Next, the module loading support. On POSIX it is and dlopen(3), this one is trivial. I think the best is to hook up into util/module.c. Currently, we explicitly call modue_call_init(type) for module types the executable is interested with. So in that call we may also load all plugins of the given type just before running all the module init functions. For modules themselves, nothing changes -- __attribute__((constructor)) does the same thing be it in a DSO or statically linked into executable. Next, and this is the most complex part. The build system for modules, and configuring it. I heard there were plans to use something like kbuild system for that, has anything been done in this context? With current config/build system, the following changes are needed: o split individual libs from libs_softmmu into their own variables. o allow obj-m (and similar) in addition to obj-y, with build flags and rules to produce an .so. o ./configure --{enable,disable}-foo -- in addition to that, I think --module-foo will be ok. If not, well, any suggestion how to name it? o modules installing o switching some modules to allow building them modular o adding more types of modules, for example a new DISPLAY type. Below is a sample implementation of the loading part (without configure checks, just POC). Thanks, /mjt --- a/util/module.c +++ b/util/module.c @@ -17,6 +17,62 @@ #include "qemu/queue.h" #include "qemu/module.h" +#define CONFIG_DSO_POSIX + +#ifdef CONFIG_DSO_POSIX + +#include +#include + +static const char *plugins_dir = "plugins"; + +static const char *module_init_types[MODULE_INIT_MAX] = { + "block", "machine", "qapi", "qom" +}; + +static void load_modules(module_init_type type) +{ + DIR *dir; + struct dirent *de; + char path[PATH_MAX]; + + const char *typestr = module_init_types[type]; + const size_t typelen = strlen(typestr); + +fprintf(stderr, "loading modules of type %s\n", typestr); + + dir = opendir(plugins_dir); + if (!dir) + { + return; + } + while((de = readdir(dir)) != NULL) + { + size_t len = strlen(de->d_name); + if (len <= typelen + || memcmp(de->d_name, typestr, typelen) != 0 + || de->d_name[typelen] != '_' + || strcmp(de->d_name + len - 3, ".so") != 0) + { + continue; + } + snprintf(path, sizeof(path), "%s/%s", plugins_dir, de->d_name); + if (dlopen(path, RTLD_NOW) == NULL) + { + fprintf(stderr, "warning: unable to load plugin %s\n", path); + continue; + } + fprintf(stderr, "loaded %s\n", path); + } + closedir(dir); +} + +#else + +static void load_modules(module_init_type type) {} + +#endif + typedef struct ModuleEntry { void (*init)(void); @@ -74,6 +130,7 @@ void module_call_init(module_init_type type) ModuleEntry *e; l = find_type(type); + load_modules(type); QTAILQ_FOREACH(e, l, node) { e->init();