From patchwork Sat May 7 23:32:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nan Li X-Patchwork-Id: 9047271 Return-Path: X-Original-To: patchwork-qemu-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 2463B9F30C for ; Mon, 9 May 2016 15:01:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E0C9C2010B for ; Mon, 9 May 2016 15:01:30 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 99667200ED for ; Mon, 9 May 2016 15:01:29 +0000 (UTC) Received: from localhost ([::1]:41778 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1azmga-000380-Rz for patchwork-qemu-devel@patchwork.kernel.org; Mon, 09 May 2016 11:01:28 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:36948) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1azhKf-0000AH-7E for qemu-devel@nongnu.org; Mon, 09 May 2016 05:18:31 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1azhKY-0004ft-UI for qemu-devel@nongnu.org; Mon, 09 May 2016 05:18:29 -0400 Received: from prv3-mh.provo.novell.com ([137.65.250.26]:39627) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1azhKY-0004fQ-IN for qemu-devel@nongnu.org; Mon, 09 May 2016 05:18:22 -0400 Received: from bogon.apac.novell.com (prv-ext-foundry1int.gns.novell.com [137.65.251.240]) by prv3-mh.provo.novell.com with ESMTP (TLS encrypted); Mon, 09 May 2016 03:18:13 -0600 From: Nan Li To: qemu-devel@nongnu.org Date: Sun, 8 May 2016 07:32:47 +0800 Message-Id: <1462663968-26607-2-git-send-email-nli@suse.com> X-Mailer: git-send-email 1.8.4.5 In-Reply-To: <1462663968-26607-1-git-send-email-nli@suse.com> References: <1462663968-26607-1-git-send-email-nli@suse.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 137.65.250.26 X-Mailman-Approved-At: Mon, 09 May 2016 11:00:46 -0400 Subject: [Qemu-devel] [PATCH 1/2] Dump: introduce a Filesystem in Userspace X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Nan Li , ptesarik@suse.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Spam-Status: No, score=-5.6 required=5.0 tests=BAYES_00, DATE_IN_PAST_24_48, RCVD_IN_DNSWL_HI,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP When running the command "dump-guest-memory", we usually need a large space of storage to save the dumpfile into disk. It costs not only much time to save a file in some of hard disks, but also costs limited storage in host. In order to reduce the saving time and make it convenient for users to dump the guest memory, we introduce a Filesystem in Userspace (FUSE) to save the dump file in RAM. It is selectable in the configure file, adding a compiling of package "fuse-devel". It doesn't change the way of dumping guest memory. qemu_fuse_main(int argc, char *argv[]) is the API for qemu code to mount this filesystem. And it only supports these operations just for dumping guest memory. static struct fuse_operations qemu_fuse_oper = { .getattr = qemu_fuse_getattr, .fgetattr = qemu_fuse_fgetattr, .readdir = qemu_fuse_readdir, .create = qemu_fuse_create, .open = qemu_fuse_open, .read = qemu_fuse_read, .write = qemu_fuse_write, .unlink = qemu_fuse_unlink, }; Signed-off-by: Nan Li --- Makefile.target | 1 + configure | 34 +++++ fuse-mem.c | 376 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fuse-mem.h | 2 + 4 files changed, 413 insertions(+) create mode 100644 fuse-mem.c create mode 100644 fuse-mem.h diff --git a/Makefile.target b/Makefile.target index 34ddb7e..7619ef8 100644 --- a/Makefile.target +++ b/Makefile.target @@ -138,6 +138,7 @@ obj-$(CONFIG_KVM) += kvm-all.o obj-y += memory.o cputlb.o obj-y += memory_mapping.o obj-y += dump.o +obj-$(CONFIG_FUSE) += fuse-mem.o obj-y += migration/ram.o migration/savevm.o LIBS := $(libs_softmmu) $(LIBS) diff --git a/configure b/configure index 5db29f0..0769caf 100755 --- a/configure +++ b/configure @@ -275,6 +275,7 @@ trace_backends="log" trace_file="trace" spice="" rbd="" +fuse="yes" smartcard="" libusb="" usb_redir="" @@ -1023,6 +1024,10 @@ for opt do ;; --enable-rbd) rbd="yes" ;; + --disable-fuse) fuse="no" + ;; + --enable-fuse) fuse="yes" + ;; --disable-xfsctl) xfs="no" ;; --enable-xfsctl) xfs="yes" @@ -1349,6 +1354,7 @@ disabled with --disable-FEATURE, default is enabled if available: vhost-net vhost-net acceleration support spice spice rbd rados block device (rbd) + fuse the support of dumping guest memory via fuse libiscsi iscsi support libnfs nfs support smartcard smartcard support (libcacard) @@ -3139,6 +3145,28 @@ EOF fi ########################################## +# fuse probe +min_fuse_version=2.9.3 +if test "$fuse" != "no" ; then + if $pkg_config --atleast-version=$min_fuse_version fuse; then + fuse_cflags=`$pkg_config fuse --cflags` + fuse_libs=`$pkg_config fuse --libs` + QEMU_CFLAGS="$fuse_cflags $QEMU_CFLAGS" + libs_softmmu="$fuse_libs $libs_softmmu" + fuse=yes + else + if $pkg_config fuse; then + if test "$fuse" = "yes" ; then + error_exit "fuse >= $min_fuse_version required for --enable-fuse" + fi + else + feature_not_found "fuse" "Please install fuse devel pkgs: fuse-devel" + fi + fuse=no + fi +fi + +########################################## # libssh2 probe min_libssh2_version=1.2.8 if test "$libssh2" != "no" ; then @@ -4815,6 +4843,7 @@ else echo "spice support $spice" fi echo "rbd support $rbd" +echo "fuse support $fuse" echo "xfsctl support $xfs" echo "smartcard support $smartcard" echo "libusb $libusb" @@ -5293,6 +5322,11 @@ if test "$rbd" = "yes" ; then echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak echo "RBD_LIBS=$rbd_libs" >> $config_host_mak fi +if test "$fuse" = "yes" ; then + echo "CONFIG_FUSE=y" >> $config_host_mak + echo "FUSE_CFLAGS=$fuse_cflags" >> $config_host_mak + echo "FUSE_LIBS=$fuse_libs" >> $config_host_mak +fi echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak if test "$coroutine_pool" = "yes" ; then diff --git a/fuse-mem.c b/fuse-mem.c new file mode 100644 index 0000000..3365ddb --- /dev/null +++ b/fuse-mem.c @@ -0,0 +1,376 @@ +/* + + gcc -Wall myfuse.c -lfuse -D_FILE_OFFSET_BITS=64 -o myfuse +*/ + +#define FUSE_USE_VERSION 26 + +#include +#include +#include +#include +#include +#include +#include "fuse-mem.h" + +//static const char *qemu_str = "Hello World!\n"; +//static const char *qemu_path = "/etc/qemu"; + +#define PAGE_SIZE (0x100000) +#define FILE_BUFFER_PAGE (PAGE_SIZE - sizeof(struct file_buffer)) + +struct file_buffer { + struct file_buffer *next; + size_t used; + size_t size; + /* Data points here */ + unsigned char data[0]; +}; + +struct file_bufhead { + //spinlock_t lock; + struct file_buffer *head; + struct file_buffer *tail; + //struct file_buffer *current; + size_t filesize; + //off_t offset; + //char *offset_ptr; +}; + +struct fuse_file { + char path[128]; + struct fuse_file_info fileinfo; + struct file_bufhead file; + struct fuse_file *next; +}; + +struct fuse_file_root { + struct fuse_file *head; + struct fuse_file *tail; +}; + +struct fuse_file_root root; + +#if 0 +void dumpfile(struct fuse_file *fuse_file_ptr) +{ + struct file_buffer *file_buffer_ptr; + int i; + printf("DUMPFILE:\n"); + for (file_buffer_ptr = fuse_file_ptr->file.head; file_buffer_ptr != NULL; file_buffer_ptr = file_buffer_ptr->next) { + for (i = 0; i < file_buffer_ptr->used; i++) { + printf("Address:0x%x: 0x%x\n", &file_buffer_ptr->data[i], file_buffer_ptr->data[i]); + } + } +} +#endif + +static int qemu_fuse_getattr(const char *path, struct stat *stbuf) +{ + struct fuse_file *fuse_file_ptr; + fuse_file_ptr = root.head; + + memset(stbuf, 0, sizeof(struct stat)); + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0777; + stbuf->st_nlink = 2; + } else { + while(fuse_file_ptr != NULL) { + if (strcmp(fuse_file_ptr->path, path) == 0) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = fuse_file_ptr->file.filesize; + return 0; + } + else + fuse_file_ptr = fuse_file_ptr->next; + } + return -ENOENT; + } + + return 0; +} +/* +static int qemu_fuse_getattr(const char *path, struct stat *stbuf) +{ + int res; + + res = lstat(path, stbuf); + if (res == -1) + return -errno; + + return 0; +} +*/ +static int qemu_fuse_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + struct fuse_file *fuse_file_ptr; + fuse_file_ptr = root.head; + + memset(stbuf, 0, sizeof(struct stat)); + + while(fuse_file_ptr != NULL) { + if (fuse_file_ptr->fileinfo.fh == fi->fh) { + stbuf->st_mode = S_IFREG | 0666; + stbuf->st_nlink = 1; + stbuf->st_size = fuse_file_ptr->file.filesize; + return 0; + } + else + fuse_file_ptr = fuse_file_ptr->next; + } + return -ENOENT; + + return 0; +} + +static int qemu_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + + return 0; +} + +static int qemu_fuse_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + struct fuse_file *fuse_file_ptr; + + fuse_file_ptr = (struct fuse_file *)malloc(sizeof(struct fuse_file)); + if (fuse_file_ptr) { + memcpy(&fuse_file_ptr->fileinfo, fi, sizeof(struct fuse_file_info)); + memset(&fuse_file_ptr->file, 0, sizeof(fuse_file_ptr->file)); + fuse_file_ptr->next = NULL; + if (root.head == NULL) { + root.head = fuse_file_ptr; + fi->fh = 1; + } else { + root.tail->next = fuse_file_ptr; + fi->fh = root.tail->fileinfo.fh + 1; + } + root.tail = fuse_file_ptr; + fuse_file_ptr->fileinfo.fh = fi->fh; + strcpy(fuse_file_ptr->path, path); + } else { + return -ENOMEM; + } + + return 0; +} + +static int qemu_fuse_open(const char *path, struct fuse_file_info *fi) +{ + struct fuse_file *fuse_file_ptr; + + fuse_file_ptr = root.head; + + while(fuse_file_ptr != NULL) { + if (strcmp(fuse_file_ptr->path, path) == 0) { + fi->fh = fuse_file_ptr->fileinfo.fh; + memcpy(&fuse_file_ptr->fileinfo, fi, sizeof(struct fuse_file_info)); + return 0; + } + else + fuse_file_ptr = fuse_file_ptr->next; + } + + return -ENOENT; +} + +static int qemu_fuse_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ +//printf("herbert:read:size=%u, offset=%u\n", size, offset); + + struct fuse_file *fuse_file_ptr; + struct file_buffer *file_buffer_ptr; + + fuse_file_ptr = root.head; + long n, count; + int item, index; + int i = 0, j = 0; + + while(fuse_file_ptr != NULL) { + if (fuse_file_ptr->fileinfo.fh == fi->fh) { + if ((fuse_file_ptr->file.filesize <= offset) || (fuse_file_ptr->file.filesize == 0)) + return 0; + if (size + offset > fuse_file_ptr->file.filesize) + size = fuse_file_ptr->file.filesize - offset; + n = size; + + item = offset / FILE_BUFFER_PAGE; + index = offset % FILE_BUFFER_PAGE; + + for (file_buffer_ptr = fuse_file_ptr->file.head; file_buffer_ptr != NULL; file_buffer_ptr = file_buffer_ptr->next) { + if ( i == item ) + break; + i++; + } + + j = index; + while (file_buffer_ptr != NULL && n > 0) { + if ( n > ((long)file_buffer_ptr->used - j) ) + count = ((long)file_buffer_ptr->used - j); + else + count = n; + + memcpy(buf + size -n, &file_buffer_ptr->data[j], count); + n -= count; + j = 0; + if (n > 0) + file_buffer_ptr = file_buffer_ptr->next; + } +//dumpfile(fuse_file_ptr); + return size; + } + else { + fuse_file_ptr = fuse_file_ptr->next; + } + } + + return -EBADF; +} + +static int qemu_fuse_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ +//printf("herbert:write:size=%u, offset=%u\n", size, offset); + + struct fuse_file *fuse_file_ptr; + struct file_buffer *file_buffer_ptr; + + long n, count; + int item, index; + int i = 0, j = 0; + + fuse_file_ptr = root.head; + + while(fuse_file_ptr != NULL) { + if (fuse_file_ptr->fileinfo.fh == fi->fh) { + n = size; + + item = offset / FILE_BUFFER_PAGE; + index = offset % FILE_BUFFER_PAGE; + + for (file_buffer_ptr = fuse_file_ptr->file.head; file_buffer_ptr != NULL; file_buffer_ptr = file_buffer_ptr->next) { + if ( i == item ) + break; + i++; + } + + j = index; + + while (file_buffer_ptr != NULL && n > 0) { + if ( n > file_buffer_ptr->size - j ) + count = file_buffer_ptr->size- j; + else + count = n; + + memcpy(&file_buffer_ptr->data[j], buf + size -n, count); + if ((count + j - (long)file_buffer_ptr->used) > 0) { + fuse_file_ptr->file.filesize += (count + j - (long)file_buffer_ptr->used); + file_buffer_ptr->used = count + j; + } + n -= count; + j = 0; + + if (n > 0) + file_buffer_ptr = file_buffer_ptr->next; + } + + while (n > 0) { + file_buffer_ptr = (struct file_buffer *)malloc(PAGE_SIZE); + if (file_buffer_ptr) { + file_buffer_ptr->next = NULL; + file_buffer_ptr->size = FILE_BUFFER_PAGE; + if ( n > file_buffer_ptr->size ) + count = file_buffer_ptr->size; + else + count = n; + + memcpy(file_buffer_ptr->data, buf + size -n, count); + + file_buffer_ptr->used = count; + + if (fuse_file_ptr->file.head == NULL) { + fuse_file_ptr->file.head = file_buffer_ptr; + } else { + fuse_file_ptr->file.tail->next = file_buffer_ptr; + } + fuse_file_ptr->file.tail = file_buffer_ptr; + fuse_file_ptr->file.filesize += count; + + n -= count; + + if (n > 0) + file_buffer_ptr = file_buffer_ptr->next; + } else { + return -ENOMEM; + } + } +//dumpfile(fuse_file_ptr); + return size; + + } + else { + fuse_file_ptr = fuse_file_ptr->next; + } + } + + return -EBADF; +} + +static int qemu_fuse_unlink(const char *path) +{ + struct fuse_file *fuse_file_ptr, *p; + struct file_buffer *file_buffer_ptr, *q; + + fuse_file_ptr = root.head; + + while(fuse_file_ptr != NULL) { + if (strcmp(fuse_file_ptr->path, path) == 0) { + + file_buffer_ptr = fuse_file_ptr->file.head; + q = fuse_file_ptr->file.head; + while (file_buffer_ptr != NULL) { + q = q->next; + free(file_buffer_ptr); + file_buffer_ptr = q; + } + + if (fuse_file_ptr == root.head) { + root.head = fuse_file_ptr->next; + } else { + for (p = root.head; p->next != fuse_file_ptr; p = p->next); + p->next = fuse_file_ptr->next; + } + + free(fuse_file_ptr); + + return 0; + } + else + fuse_file_ptr = fuse_file_ptr->next; + } + + return -EACCES; +} + +static struct fuse_operations qemu_fuse_oper = { + .getattr = qemu_fuse_getattr, + .fgetattr = qemu_fuse_fgetattr, + .readdir = qemu_fuse_readdir, + .create = qemu_fuse_create, + .open = qemu_fuse_open, + .read = qemu_fuse_read, + .write = qemu_fuse_write, + .unlink = qemu_fuse_unlink, +}; + +extern int qemu_fuse_main(int argc, char *argv[]) +{ + return fuse_main(argc, argv, &qemu_fuse_oper, NULL); +} + + + diff --git a/fuse-mem.h b/fuse-mem.h new file mode 100644 index 0000000..1a40168 --- /dev/null +++ b/fuse-mem.h @@ -0,0 +1,2 @@ +extern int qemu_fuse_main(int argc, char *argv[]); +