qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Nan Li <nli@suse.com>
To: qemu-devel@nongnu.org
Cc: ptesarik@suse.com, Nan Li <nli@suse.com>
Subject: [Qemu-devel] [PATCH 1/2] Dump: introduce a Filesystem in Userspace
Date: Sun,  8 May 2016 07:32:47 +0800	[thread overview]
Message-ID: <1462663968-26607-2-git-send-email-nli@suse.com> (raw)
In-Reply-To: <1462663968-26607-1-git-send-email-nli@suse.com>

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 <nli@suse.com>
---
 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 <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#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[]);
+
-- 
1.8.4.5

  reply	other threads:[~2016-05-09  9:18 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-07 23:32 [Qemu-devel] [PATCH 0/2] Dump: add a Filesystem in Userspace and command "fuse-mount" Nan Li
2016-05-07 23:32 ` Nan Li [this message]
2016-05-09 15:52   ` [Qemu-devel] [PATCH 1/2] Dump: introduce a Filesystem in Userspace Eric Blake
2016-05-09 16:13     ` Daniel P. Berrange
2016-05-09 16:20       ` Petr Tesarik
2016-05-09 16:32         ` Daniel P. Berrange
2016-05-10  6:19           ` Petr Tesarik
2016-05-10  8:39             ` Daniel P. Berrange
2016-05-10  5:59     ` Petr Tesarik
2016-05-10  8:48       ` Daniel P. Berrange
2016-05-10  9:42         ` Petr Tesarik
2016-05-10 11:26           ` Nan Li
2016-05-10  9:56       ` Stefan Hajnoczi
2016-05-10 11:02         ` Nan Li
2016-05-10 11:55         ` Petr Tesarik
2016-05-12 10:09           ` Stefan Hajnoczi
2016-05-12 10:30             ` Petr Tesarik
2016-05-07 23:32 ` [Qemu-devel] [PATCH 2/2] Dump: add command "fuse-mount" Nan Li
2016-05-09 16:30   ` Daniel P. Berrange
2016-05-09 16:48   ` Eric Blake
2016-05-10 11:32     ` Nan Li
2016-05-10  9:47 ` [Qemu-devel] [PATCH 0/2] Dump: add a Filesystem in Userspace and " Stefan Hajnoczi
2016-05-10  9:50 ` Stefan Hajnoczi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1462663968-26607-2-git-send-email-nli@suse.com \
    --to=nli@suse.com \
    --cc=ptesarik@suse.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).