Hi folks, Here are a bunch of patches which start adding xen support to qemu. Overview (individual patches have longer descriptions): #1 -- groundwork: build system, cmd line options, ... #2 -- xen backend driver infrastructrure #3 -- xen console backend driver #4 -- xen framebuffer backend driver #5 -- xen block backend driver #6 -- xen nic backend driver #7 -- allow xen disks and nics being configured via qemu command line options. With the first four patches in place upstream qemu can replace xen''s qemu-dm for paravirtual domains. The block and nic backend drivers are full userspace implementations using the grant table device (gntdev). xen support is implemented using another machine type. xen''s qemu-dm already uses the machine type to switch between paravirtualized and fully virtualized machines, so this was the natural choice. qemu has gets a new "xenpv" machine type additionally to the "pc" and "isapc" ones. I''ve placed the new files into the hw/ directory. With all my xen bits (partly not submitted yet with these patches) those add up to 18 new files, all prefixed with xen. Hmm, maybe I should better place them somewhere else, so the already quite crowded hw/ directory doesn''t become even more cluttered? If so, any suggestions? New toplevel directory? New subdirectory below hw/? Something else? Comments? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 13:17 UTC
[Xen-devel] [PATCH 1/7] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -domid command line option. - allow xenpv machines run without disk and kernel specified. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 8 +++++ configure | 27 ++++++++++++++++ hw/boards.h | 4 ++ hw/xen-machine.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen.h | 12 +++++++ target-i386/machine.c | 3 ++ vl.c | 25 +++++++++++++-- 7 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 hw/xen-machine.c create mode 100644 hw/xen.h diff --git a/Makefile.target b/Makefile.target index ff105c1..4f42582 100644 --- a/Makefile.target +++ b/Makefile.target @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) LIBS += $(CONFIG_VNC_TLS_LIBS) endif +# xen backend driver support +XEN_OBJS := xen-machine.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index fd04766..34516b9 100755 --- a/configure +++ b/configure @@ -108,6 +108,7 @@ uname_release="" curses="yes" nptl="yes" mixemu="no" +xen="no" # OS specific targetos=`uname -s` @@ -202,6 +203,7 @@ linux="yes" linux_user="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" + xen="yes" audio_possible_drivers="$audio_possible_drivers fmod" fi ;; @@ -285,6 +287,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --enable-profiler) profiler="yes" @@ -421,6 +425,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of additional emulated audio cards" echo " Available cards: ac97 adlib cs4231a gus" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" @@ -681,6 +686,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xs.h> +#include <xenctrl.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -916,6 +937,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1167,6 +1189,11 @@ if test "$brlapi" = "yes" ; then echo "#define CONFIG_BRLAPI 1" >> $config_h echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak fi +if test "$xen" = "yes" ; then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then diff --git a/hw/boards.h b/hw/boards.h index 22ac332..5931720 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -29,6 +29,10 @@ extern QEMUMachine bareetraxfs_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; +extern QEMUMachine xenfv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen-machine.c b/hw/xen-machine.c new file mode 100644 index 0000000..88f0f6e --- /dev/null +++ b/hw/xen-machine.c @@ -0,0 +1,83 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007,08 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "boards.h" + +#include "xen.h" + +/* -------------------------------------------------------------------- */ +/* variables */ + +int xen_domid; + +/* -------------------------------------------------------------------- */ +/* paravirtualized xen guests */ + +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + + /* create dummy cpu, halted */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; +} + +QEMUMachine xenpv_machine = { + "xenpv", + "paravirtualized Xen machine", + xenpv_init, +}; + +/* -------------------------------------------------------------------- */ +/* fully virtualized xen guests */ + +static void xenfv_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + /* to be done */ + fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__); +} + +QEMUMachine xenfv_machine = { + "xenfv", + "fully virtualized Xen machine", + xenfv_init, +}; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..19349b5 --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,12 @@ +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * should not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + * + * internal bits for the xen backend drivers are in xen-backend.h + */ + +/* xen-machine.c */ +extern int xen_domid; + diff --git a/target-i386/machine.c b/target-i386/machine.c index 91dbd55..98ece17 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index 8801615..8aef3bd 100644 --- a/vl.c +++ b/vl.c @@ -29,6 +29,7 @@ #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" +#include "hw/xen.h" #include "net.h" #include "console.h" #include "sysemu.h" @@ -7737,6 +7738,9 @@ static void help(int exitcode) "-startdate select initial date of the clock\n" "-icount [N|auto]\n" " Enable virtual instruction counter with 2^N clock ticks per instruction\n" +#ifdef CONFIG_XEN + "-domid specify xen guest domain id\n" +#endif "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7842,6 +7846,9 @@ enum { QEMU_OPTION_startdate, QEMU_OPTION_tb_size, QEMU_OPTION_icount, +#ifdef CONFIG_XEN + QEMU_OPTION_domid, +#endif }; typedef struct QEMUOption { @@ -7930,6 +7937,9 @@ const QEMUOption qemu_options[] = { #ifdef CONFIG_CURSES { "curses", 0, QEMU_OPTION_curses }, #endif +#ifdef CONFIG_XEN + { "domid", HAS_ARG, QEMU_OPTION_domid }, +#endif /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -8150,7 +8160,7 @@ int main(int argc, char **argv) #endif uint32_t boot_devices_bitmap = 0; int i; - int snapshot, linux_boot, net_boot; + int snapshot, linux_boot, net_boot, nodisk_ok; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; const char *boot_devices = ""; @@ -8787,6 +8797,11 @@ int main(int argc, char **argv) icount_time_shift = strtol(optarg, NULL, 0); } break; +#ifdef CONFIG_XEN + case QEMU_OPTION_domid: + xen_domid = atoi(optarg); + break; +#endif } } } @@ -8852,9 +8867,13 @@ int main(int argc, char **argv) linux_boot = (kernel_filename != NULL); net_boot = (boot_devices_bitmap >> (''n'' - ''a'')) & 0xF; - /* XXX: this should not be: some embedded targets just have flash */ + /* need a disk for this machine to boot ? */ + /* XXX: add embedded targets which just have flash */ + nodisk_ok = 0; + if (0 == strcmp(machine->name, "xenpv")) + nodisk_ok = 1; if (!linux_boot && net_boot == 0 && - nb_drives_opt == 0) + !nodisk_ok && nb_drives_opt == 0) help(1); if (!linux_boot && *kernel_cmdline != ''\0'') { -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/list.h | 169 ++++++++++++++ hw/xen-backend.c | 663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-backend.h | 116 ++++++++++ hw/xen-machine.c | 5 +- 5 files changed, 953 insertions(+), 2 deletions(-) create mode 100644 hw/list.h create mode 100644 hw/xen-backend.c create mode 100644 hw/xen-backend.h diff --git a/Makefile.target b/Makefile.target index 4f42582..0451048 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o +XEN_OBJS := xen-machine.o xen-backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/list.h b/hw/list.h new file mode 100644 index 0000000..fa9f790 --- /dev/null +++ b/hw/list.h @@ -0,0 +1,169 @@ +#ifndef _LIST_H +#define _LIST_H 1 + +/* + * Simple doubly linked list implementation. + * -- shamelessly stolen from the linux kernel sources + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a item entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * item, + struct list_head * prev, + struct list_head * next) +{ + next->prev = item; + item->next = next; + item->prev = prev; + prev->next = item; +} + +/** + * list_add - add a item entry + * @item: item entry to be added + * @head: list head to add it after + * + * Insert a item entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *item, struct list_head *head) +{ + __list_add(item, head, head->next); +} + +/** + * list_add_tail - add a item entry + * @item: item entry to be added + * @head: list head to add it before + * + * Insert a item entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *item, struct list_head *head) +{ + __list_add(item, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the item list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev - iterate over a list in reverse order + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +#endif /* _LIST_H */ diff --git a/hw/xen-backend.c b/hw/xen-backend.c new file mode 100644 index 0000000..4819624 --- /dev/null +++ b/hw/xen-backend.c @@ -0,0 +1,663 @@ +/* + * xen backend driver infrastructure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static LIST_HEAD(xendevs); +static const char *root = "/local/domain/0/backend"; +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + free(val); + return rc; +} + +int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct xendev *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct xendev *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct xendev *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (0 != rc) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct xendev *xen_be_find_xendev(char *type, int dom, int dev) +{ + struct xendev *xendev; + struct list_head *item; + + list_for_each(item, &xendevs) { + xendev = list_entry(item, struct xendev, next); + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct xendev *xen_be_get_xendev(char *type, int dom, int dev, + struct devops *ops) +{ + struct xendev *xendev; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = malloc(ops->size); + memset(xendev,0,ops->size); + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d", + root, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can''t open evtchn device\n"); + free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + list_add_tail(&xendev->next, &xendevs); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct xendev *xen_be_del_xendev(int dom, int dev) +{ + struct xendev *xendev; + struct list_head *item, *tmp; + + list_for_each_safe(item, tmp, &xendevs) { + xendev = list_entry(item, struct xendev, next); + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + list_del(&xendev->next); + free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct xendev *xendev, const char *node) +{ + if (NULL == node || 0 == strcmp(node, "online")) { + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct xendev *xendev, const char *node) +{ + int fe_state; + + if (NULL == node || 0 == strcmp(node, "state")) { + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (NULL == node || 0 == strcmp(node, "protocol")) { + free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct xendev *xendev) +{ + char token[BUFSIZE]; + int be_state; + + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (NULL == xendev->fe) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct xendev *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct xendev *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + +#if 0 + if (xendev->ops->event) + xendev->ops->event(xendev); +#endif + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct xendev *xendev) +{ + if (xendev->be_state == XenbusStateClosed) + return; + if (xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + xen_be_set_state(xendev, XenbusStateClosed); +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct xendev *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(char *type, int dom, struct devops *ops) +{ + struct xendev *xendev; + char path[BUFSIZE], token[BUFSIZE]; + char **dev = NULL; + unsigned int cdev, j; + + /* setup watch */ + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (NULL == xendev) + continue; + xen_be_check_state(xendev); + } + free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct devops *ops) +{ + struct xendev *xendev; + char path[BUFSIZE]; + unsigned int len, dev; + + len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (-1 == dev) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct xendev *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (NULL == vec) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct xendev *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (-1 == xen_xc) { + fprintf(stderr, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(char *type, struct devops *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct xendev *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (-1 == xendev->local_port) { + xendev->local_port = 0; + return -1; + } + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); +#if 0 + xc_evtchn_unmask(xendev->evtchndev, xendev->local_port); +#endif + return 0; +} + +void xen_be_unbind_evtchn(struct xendev *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct xendev *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen-backend.h b/hw/xen-backend.h new file mode 100644 index 0000000..db36ae7 --- /dev/null +++ b/hw/xen-backend.h @@ -0,0 +1,116 @@ +#include <stddef.h> +#include <xs.h> +#include <xenctrl.h> +#include <xen/elfnote.h> +#include <xen/elfstructs.h> + +#include <xen/io/xenbus.h> + +#include "list.h" + +#include "hw.h" +#include "xen.h" + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +/* ------------------------------------------------------------- */ + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* ------------------------------------------------------------- */ + +#define BUFSIZE 1024 + +struct xendev; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct devops { + size_t size; + uint32_t flags; + void (*alloc)(struct xendev *xendev); + int (*init)(struct xendev *xendev); + int (*connect)(struct xendev *xendev); + void (*event)(struct xendev *xendev); + void (*disconnect)(struct xendev *xendev); + int (*free)(struct xendev *xendev); + void (*backend_changed)(struct xendev *xendev, const char *node); + void (*frontend_changed)(struct xendev *xendev, const char *node); +}; + +struct xendev { + char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct devops *ops; + struct list_head next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_domid; /* set by cmd line option, in vl.c */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct xendev *xendev, const char *node); +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct xendev *xendev, const char *node); +int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct xendev *xen_be_find_xendev(char *type, int dom, int dev); +void xen_be_check_state(struct xendev *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(char *type, struct devops *ops); +int xen_be_set_state(struct xendev *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct xendev *xendev); +void xen_be_unbind_evtchn(struct xendev *xendev); +int xen_be_send_notify(struct xendev *xendev); +void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 88f0f6e..798c0a7 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -25,7 +25,7 @@ #include "hw.h" #include "boards.h" -#include "xen.h" +#include "xen-backend.h" /* -------------------------------------------------------------------- */ /* variables */ @@ -54,6 +54,9 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, } env = cpu_init(cpu_model); env->halted = 1; + + /* setup xen backend handlers */ + xen_be_init(); } QEMUMachine xenpv_machine = { -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 13:17 UTC
[Xen-devel] [PATCH 3/7] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen-backend.h | 3 + hw/xen-console.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 2 + 4 files changed, 277 insertions(+), 0 deletions(-) create mode 100644 hw/xen-console.c diff --git a/Makefile.target b/Makefile.target index 0451048..05619fa 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,6 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS += xen-console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index db36ae7..55ffd31 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -114,3 +114,6 @@ int xen_be_send_notify(struct xendev *xendev); void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +struct devops xen_console_ops; /* xen_console.c */ + diff --git a/hw/xen-console.c b/hw/xen-console.c new file mode 100644 index 0000000..9c67f1b --- /dev/null +++ b/hw/xen-console.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <malloc.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct xen_console { + struct xendev xendev; /* must be first */ + struct buffer buffer; + char console[BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct xen_console *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct xen_console *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct xen_console *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct xen_console *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct xen_console *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct xendev *xendev) +{ + struct xen_console *con = container_of(xendev, struct xen_console, xendev); + char *type; + + if (!serial_hds[con->xendev.dev]) { + xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev); + return -1; + } + + /* setup */ + snprintf(con->console, sizeof(con->console), + "/local/domain/%d/console", con->xendev.dom); + con->chr = serial_hds[con->xendev.dev]; + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + return 0; +} + +static int con_connect(struct xendev *xendev) +{ + struct xen_console *con = container_of(xendev, struct xen_console, xendev); + int limit; + + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) + return -1; + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) + return -1; + if (0 == xenstore_read_int(con->console, "limit", &limit)) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct xendev *xendev) +{ + struct xen_console *con = container_of(xendev, struct xen_console, xendev); + + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct xendev *xendev) +{ + struct xen_console *con = container_of(xendev, struct xen_console, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct devops xen_console_ops = { + .size = sizeof(struct xen_console), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 798c0a7..da10982 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -57,6 +57,8 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, /* setup xen backend handlers */ xen_be_init(); + + xen_be_register("console", &xen_console_ops); } QEMUMachine xenpv_machine = { -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 13:17 UTC
[Xen-devel] [PATCH 4/7] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than unstable (aka son-to-be 3.3). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 4 + hw/xen-framebuffer.c | 925 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 5 + 4 files changed, 935 insertions(+), 1 deletions(-) create mode 100644 hw/xen-framebuffer.c diff --git a/Makefile.target b/Makefile.target index 05619fa..66d41ee 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o +XEN_OBJS += xen-console.o xen-framebuffer.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 55ffd31..3facf90 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) /* actual backend drivers */ struct devops xen_console_ops; /* xen_console.c */ +struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ +struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c new file mode 100644 index 0000000..716eafd --- /dev/null +++ b/hw/xen-framebuffer.c @@ -0,0 +1,925 @@ +/* + * xen paravirt framebuffer backend + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct xen_common { + struct xendev xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct xen_input { + struct xen_common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct xen_fb { + struct xen_common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int feature_update; + int refresh_period; + int bug_trigger; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct xen_common *c) +{ + int mfn; + + if (-1 == xenstore_read_fe_int(&c->xendev, "page-ref", &mfn)) + return -1; + if (-1 == xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port)) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (NULL == c->page) + return -1; + return 0; +} + +static void common_unbind(struct xen_common *c) +{ + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } + xen_be_unbind_evtchn(&c->xendev); +} + +/* -------------------------------------------------------------------- */ + +/* + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; + +static unsigned char scancode2linux[512]; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct xen_input *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct xen_input *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct xen_input *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct xen_input *xenfb, + int abs_x, int abs_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.pos.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct xen_input *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct xen_input *xenfb = opaque; + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (xenfb->c.ds->width - 1) / 0x7fff, + dy * (xenfb->c.ds->height - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct xendev *xendev) +{ + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); + static int first = 1; + int i; + + if (first) { + /* Prepare scancode mapping table */ + for (i = 0; i < 128; i++) { + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + scancode2linux[i | 0x80] + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + } + first = 0; + } + + if (!in->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct xendev *xendev) +{ + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "request-abs-pointer", &in->abs_pointer_wanted)) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (0 != rc) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct xendev *xendev) +{ + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct xendev *xendev) +{ + struct xen_input *xenfb = container_of(xendev, struct xen_input, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct xen_fb *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbmfns; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (0 == ptr32[1]) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = malloc(sizeof(unsigned long) * n_fbdirs); + fbmfns = malloc(sizeof(unsigned long) * n_fbmfns); + if (!pgmfns || !fbmfns) + goto out; + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + if (pgmfns) + free(pgmfns); + if (fbmfns) + free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct xen_fb *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride < 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(xenfb->c.ds->data \ + + (line * xenfb->c.ds->linesize) \ + + (x * xenfb->c.ds->depth / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + xenfb->c.ds->depth / 8); \ + } \ + } + + +/* This copies data from the guest framebuffer region, into QEMU''s copy + * NB. QEMU''s copy is stored in the pixel format of a) the local X + * server (SDL case) or b) the current VNC client pixel format. + * When shifting between colour depths we preserve the MSB. + */ +static void xenfb_guest_copy(struct xen_fb *xenfb, int x, int y, int w, int h) +{ + int line; + + if (1 /* !xenfb->c.ds->shared_buf */) { + if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */ + for (line = y ; line < (y+h) ; line++) { + memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8), + xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8), + w * xenfb->depth / 8); + } + } else { /* Mismatch requires slow pixel munging */ + /* 8 bit == r:3 g:3 b:2 */ + /* 16 bit == r:5 g:6 b:5 */ + /* 24 bit == r:8 g:8 b:8 */ + /* 32 bit == r:8 g:8 b:8 (padding:8) */ + if (xenfb->depth == 8) { + if (xenfb->c.ds->depth == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } + } else if (xenfb->depth == 16) { + if (xenfb->c.ds->depth == 8) { + BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8); + } + } else if (xenfb->depth == 24 || xenfb->depth == 32) { + if (xenfb->c.ds->depth == 8) { + BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2); + } else if (xenfb->c.ds->depth == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } + } + } + } + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct xen_fb *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct xen_fb *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct xen_fb *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct xen_fb *xenfb = opaque; + int i; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period; + + if (xenfb_queue_full(xenfb)) + return; + + /* + * TODO: xen''s qemu-dm seems to have some patches to + * make the qemu display code avoid unneeded + * work. + * - Port them over. + * - Put ds->idle back into use then. + * - Same goes for ds->shared_buf btw. + */ + if (0 /* xenfb->c.ds->idle */) + period = XENFB_NO_REFRESH; + else { + period = xenfb->c.ds->gui_timer_interval; + if (!period) + period = 30; // GUI_REFRESH_INTERVAL (see vl.c) + } + + if (xenfb->refresh_period != period) { + dprintf("%s: %d\n", __FUNCTION__, period); + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) { + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n"); + dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +static void xenfb_handle_events(struct xen_fb *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "xen be: %s: bogus update ignored\n", + xenfb->c.xendev.name); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "xen be: %s: bogus update clipped\n", + xenfb->c.xendev.name); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct xen_fb *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static int fb_init(struct xendev *xendev) +{ + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); + + if (!fb->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct xendev *xendev) +{ + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update)) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + if (-1 == xenstore_read_fe_int(xendev, "videoram", &videoram)) + videoram = 0; + + rc = common_bind(&fb->c); + if (0 != rc) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (0 != rc) + return rc; + + rc = xenfb_map_fb(fb); + if (0 != rc) + return rc; + + graphic_console_init(fb->c.ds, + xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + + return 0; +} + +static void fb_disconnect(struct xendev *xendev) +{ + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); + + /* FIXME: Hmm, un-init gfx display? can qemu handle that? */ + common_unbind(&fb->c); +} + +static void fb_frontend_changed(struct xendev *xendev, const char *node) +{ + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (0 == fb->bug_trigger && 0 == strcmp(node, "state") && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct xendev *xendev) +{ + struct xen_fb *xenfb = container_of(xendev, struct xen_fb, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct devops xen_kbdmouse_ops = { + .size = sizeof(struct xen_input), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct devops xen_framebuffer_ops = { + .size = sizeof(struct xen_fb), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +static void xen_set_display_type(int domid, char *type, DisplayState *ds) +{ + struct xendev *xendev; + struct xen_common *c; + + xendev = xen_be_find_xendev(type, domid, 0); + if (!xendev) + return; + c = container_of(xendev, struct xen_common, xendev); + c->ds = ds; + xen_be_printf(xendev, 1, "ds is %p\n", ds); + /* retry ->init() */ + xen_be_check_state(xendev); +} + +void xen_set_display(int domid, DisplayState *ds) +{ + xen_set_display_type(domid, "vkbd", ds); + xen_set_display_type(domid, "vfb", ds); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index da10982..c03cb53 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -59,6 +59,11 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, xen_be_init(); xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_set_display(xen_domid, ds); } QEMUMachine xenpv_machine = { -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 13:17 UTC
[Xen-devel] [PATCH 5/7] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 2 + hw/xen-blkif.h | 103 ++++++++ hw/xen-disk.c | 681 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 1 + sysemu.h | 2 +- vl.c | 4 + 7 files changed, 793 insertions(+), 2 deletions(-) create mode 100644 hw/xen-blkif.h create mode 100644 hw/xen-disk.c diff --git a/Makefile.target b/Makefile.target index 66d41ee..2d599d2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 3facf90..941b0a6 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -10,6 +10,7 @@ #include "hw.h" #include "xen.h" +#include "sysemu.h" /* * tweaks needed to build with different xen versions @@ -118,6 +119,7 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) struct devops xen_console_ops; /* xen_console.c */ struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ +struct devops xen_blkdev_ops; /* xen_disk.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-blkif.h b/hw/xen-blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen-blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen-disk.c b/hw/xen-disk.c new file mode 100644 index 0000000..6947363 --- /dev/null +++ b/hw/xen-disk.c @@ -0,0 +1,681 @@ +/* + * xen paravirt block device backend + * + * FIXME: the code is designed to handle multiple outstanding + * requests (using aio or using threads), which isn''t used right + * now due to limitations of the qemu block driver interface. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/utsname.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "list.h" + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen-blkif.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start, end; + struct iovec vec[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int vecs; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + struct blkdev *blkdev; + struct list_head list; +}; + +struct blkdev { + struct xendev xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *fileproto; + char *filename; + int ring_ref; + void *sring; + int file; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + struct list_head inflight; + struct list_head finished; + struct list_head freelist; + int requests; + + /* qemu block driver */ + BlockDriverState *bs; +}; + +static int syncwrite = 0; +static int batch_maps = 0; +static int max_requests = 32; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct blkdev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (list_empty(&blkdev->freelist)) { + if (blkdev->requests >= max_requests) + goto out; + /* allocate new struct */ + ioreq = malloc(sizeof(*ioreq)); + if (NULL == ioreq) + goto out; + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + blkdev->requests++; + } else { + /* get one from freelist */ + ioreq = list_entry(blkdev->freelist.next, struct ioreq, list); + list_del(&ioreq->list); + } + list_add_tail(&ioreq->list, &blkdev->inflight); + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct blkdev *blkdev = ioreq->blkdev; + + list_del(&ioreq->list); + list_add_tail(&ioreq->list, &blkdev->finished); +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct blkdev *blkdev = ioreq->blkdev; + + list_del(&ioreq->list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + list_add_tail(&ioreq->list, &blkdev->freelist); +} + +/* + * translate request into iovec + start offset + end offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct blkdev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->end = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + + ioreq->vec[i].iov_base = (void*)mem; + ioreq->vec[i].iov_len = len; + ioreq->end += len; + } + if (ioreq->end > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + ioreq->vecs = i; + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + if (!ioreq->pages) + return; + if (0 != xc_gnttab_munmap(gnt, ioreq->pages, ioreq->vecs)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->vecs; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->vecs; i++) { + if (!ioreq->page[i]) + continue; + if (0 != xc_gnttab_munmap(gnt, ioreq->page[i], 1)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->vecs, ioreq->domids, ioreq->refs, ioreq->prot); + if (NULL == ioreq->pages) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->vecs, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->vecs; i++) + ioreq->vec[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->vecs; + } else { + for (i = 0; i < ioreq->vecs; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (NULL == ioreq->page[i]) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->vec[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu(struct ioreq *ioreq) +{ + struct blkdev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (-1 == ioreq_map(ioreq)) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct blkdev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct blkdev *blkdev) +{ + struct list_head *item, *safe; + struct ioreq *ioreq; + int send_notify = 0; + + list_for_each_safe(item, safe, &blkdev->finished) { + ioreq = list_entry(item, struct ioreq, list); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct blkdev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct blkdev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + do { + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + /* Limit #of requests we queue up for I/O so we ack requests + * faster if busy. Improves backend/frontend parallelism and + * reduces evchn signaling. */ + if (rp > rc + (max_requests >> 2)) { + rp = rc + (max_requests >> 2); + blkdev->more_work++; + } + + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (NULL == ioreq) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (0 != ioreq_parse(ioreq)) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + /* run i/o in qemu mode */ + ioreq_runio_qemu(ioreq); + ioreq_finish(ioreq); + } + blk_send_response_all(blkdev); + + } while (blkdev->more_work); +} + +/* ------------------------------------------------------------- */ + +static void blk_alloc(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + + INIT_LIST_HEAD(&blkdev->inflight); + INIT_LIST_HEAD(&blkdev->finished); + INIT_LIST_HEAD(&blkdev->freelist); +} + +static int blk_init(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + int mode, qflags, have_barriers, index, info = 0; + char *h; + + /* read xenstore entries */ + if (NULL == blkdev->params) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (NULL != (h = strchr(blkdev->params, '':''))) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (NULL == blkdev->mode) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (NULL == blkdev->type) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (NULL == blkdev->dev) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + + /* do we have all we need? */ + if (NULL == blkdev->params || + NULL == blkdev->mode || + NULL == blkdev->type || + NULL == blkdev->dev) + return -1; + + /* read-only ? */ + if (0 == strcmp(blkdev->mode, "w")) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; + } + + /* init qemu block driver */ + index = (blkdev->xendev.dev - 202 * 256) / 16; + index = drive_get_index(IF_XEN, 0, index); + if (-1 == index) { + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (0 != bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto))) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + blkdev->bs = drives_table[index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) + blkdev->file_size = 0; + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port)) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32)) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64)) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + + if (-1 != blkdev->file) { + close(blkdev->file); + blkdev->file = -1; + } + if (blkdev->bs) { + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + struct list_head *item, *safe; + struct ioreq *ioreq; + + list_for_each_safe(item, safe, &blkdev->freelist) { + ioreq = list_entry(item, struct ioreq, list); + list_del(&ioreq->list); + free(ioreq); + } + + free(blkdev->params); + free(blkdev->mode); + return 0; +} + +static void blk_event(struct xendev *xendev) +{ + struct blkdev *blkdev = container_of(xendev, struct blkdev, xendev); + blk_handle_requests(blkdev); +} + +struct devops xen_blkdev_ops = { + .size = sizeof(struct blkdev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index c03cb53..1b647a2 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -61,6 +61,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/sysemu.h b/sysemu.h index b12fae0..49e75b1 100644 --- a/sysemu.h +++ b/sysemu.h @@ -113,7 +113,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_XEN } BlockInterfaceType; typedef struct DriveInfo { diff --git a/vl.c b/vl.c index 8aef3bd..7187545 100644 --- a/vl.c +++ b/vl.c @@ -5478,6 +5478,9 @@ static int drive_init(struct drive_opt *arg, int snapshot, } else if (!strcmp(buf, "sd")) { type = IF_SD; max_devs = 0; + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; @@ -5663,6 +5666,7 @@ static int drive_init(struct drive_opt *arg, int snapshot, switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 1 + hw/xen-machine.c | 1 + hw/xen-nic.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 451 insertions(+), 1 deletions(-) create mode 100644 hw/xen-nic.c diff --git a/Makefile.target b/Makefile.target index 2d599d2..281d7fa 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 941b0a6..2db2c3c 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -120,6 +120,7 @@ struct devops xen_console_ops; /* xen_console.c */ struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ struct devops xen_blkdev_ops; /* xen_disk.c */ +struct devops xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 1b647a2..3fa4079 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -62,6 +62,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/hw/xen-nic.c b/hw/xen-nic.c new file mode 100644 index 0000000..56d1474 --- /dev/null +++ b/hw/xen-nic.c @@ -0,0 +1,448 @@ +/* + * xen paravirt network card backend + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct netdev { + struct xendev xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +#define PROTO_TCP 6 +#define PROTO_UDP 17 + +static uint32_t checksum_add(int len, uint8_t *buf) +{ + uint32_t sum = 0; + int i; + + for (i = 0; i < len; i++) { + if (i & 1) + sum += (uint32_t)buf[i]; + else + sum += (uint32_t)buf[i] << 8; + } + return sum; +} + +static uint16_t checksum_finish(uint32_t sum) +{ + while (sum>>16) + sum = (sum & 0xFFFF)+(sum >> 16); + return ~sum; +} + +static uint16_t checksum_tcpudp(uint16_t length, uint16_t proto, + uint8_t *addrs, uint8_t *buf) +{ + uint32_t sum = 0; + + sum += checksum_add(length, buf); // payload + sum += checksum_add(8, addrs); // src + dst address + sum += proto + length; // protocol & length + return checksum_finish(sum); +} + +static void checksum_calculate(uint8_t *data, int length) +{ + int hlen, plen, proto, csum_offset; + uint16_t csum; + + if ((data[14] & 0xf0) != 0x40) + return; /* not IPv4 */ + hlen = (data[14] & 0x0f) * 4; + plen = (data[16] << 8 | data[17]) - hlen; + proto = data[23]; + + switch (proto) { + case PROTO_TCP: + csum_offset = 16; + break; + case PROTO_UDP: + csum_offset = 6; + break; + default: + return; + } + + if (plen < csum_offset+2) + return; + + data[14+hlen+csum_offset] = 0; + data[14+hlen+csum_offset+1] = 0; + csum = checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); + data[14+hlen+csum_offset] = csum >> 8; + data[14+hlen+csum_offset+1] = csum & 0xff; +} + + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct netdev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct netdev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct netdev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) + checksum_calculate(page + txreq.offset, txreq.size); + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct netdev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct netdev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct netdev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct xendev *xendev) +{ + struct netdev *netdev = container_of(xendev, struct netdev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (NULL == netdev->mac) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (NULL == netdev->mac) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, net_rx_packet, net_rx_ok, netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct xendev *xendev) +{ + struct netdev *netdev = container_of(xendev, struct netdev, xendev); + int rx_copy; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref)) + return 1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port)) + return -1; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy)) + rx_copy = 0; + if (0 == rx_copy) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct xendev *xendev) +{ + struct netdev *netdev = container_of(xendev, struct netdev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } +} + +static void net_event(struct xendev *xendev) +{ + struct netdev *netdev = container_of(xendev, struct netdev, xendev); + net_tx_packets(netdev); +} + +/* ------------------------------------------------------------- */ + +struct devops xen_netdev_ops = { + .size = sizeof(struct netdev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, +}; -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 13:17 UTC
[Xen-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 7 +++ hw/xen-config.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 22 ++++++++- 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 hw/xen-config.c diff --git a/Makefile.target b/Makefile.target index 281d7fa..acb679d 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS := xen-machine.o xen-backend.o xen-config.o XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 2db2c3c..2f21f84 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -11,6 +11,8 @@ #include "hw.h" #include "xen.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* * tweaks needed to build with different xen versions @@ -124,3 +126,8 @@ struct devops xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + diff --git a/hw/xen-config.c b/hw/xen-config.c new file mode 100644 index 0000000..112cfcf --- /dev/null +++ b/hw/xen-config.c @@ -0,0 +1,140 @@ +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + struct list_head list; +}; +static LIST_HEAD (xs_cleanup); + +static int xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = malloc(sizeof(*d)); + d->xs_dir = dir; + list_add_tail(&d->list, &xs_cleanup); + return 0; +} + +void xen_config_cleanup(void) +{ + struct list_head *item; + struct xs_dirs *d; + + fprintf(stderr, "xen be: %s\n", __FUNCTION__); + list_for_each(item, &xs_cleanup) { + d = list_entry(item, struct xs_dirs, list); + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms = { + .id = xen_domid, + .perms = p, + }; + + if (!xs_mkdir(xenstore, 0, dev)) { + fprintf(stderr, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, &perms, 1)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(char *ftype, char *btype, int vdev, + char *fe, char *be, int len) +{ + snprintf(fe, len, "/local/domain/%d/device/%s/%d", + xen_domid, ftype, vdev); + snprintf(be, len, "/local/domain/0/backend/%s/%d/%d", + btype, xen_domid, vdev); + + xen_config_dev_mkdir(fe, XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ +#if 0 + xenstore_write_str(fe, "protocol", + xen_config_dev_protocol(xen)); +#endif + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + char *devtype = cdrom ? "cdrom" : "disk"; + char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + fprintf(stderr, "xen be: config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + fprintf(stderr, "xen be: config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 3fa4079..380a61b 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -43,6 +43,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; + int i, index; /* create dummy cpu, halted */ if (cpu_model == NULL) { @@ -55,9 +56,28 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, env = cpu_init(cpu_model); env->halted = 1; - /* setup xen backend handlers */ + /* backend core init */ xen_be_init(); + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (nd_table[i].model && 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); + + /* backend driver init */ xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); -- 1.5.4.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:04 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Gerd Hoffmann wrote:> - configure script and build system changes. > - wind up new machine type. > - add -domid command line option. > - allow xenpv machines run without disk and kernel specified. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 8 +++++ > configure | 27 ++++++++++++++++ > hw/boards.h | 4 ++ > hw/xen-machine.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen.h | 12 +++++++ > target-i386/machine.c | 3 ++ > vl.c | 25 +++++++++++++-- > 7 files changed, 159 insertions(+), 3 deletions(-) > create mode 100644 hw/xen-machine.c > create mode 100644 hw/xen.h > > diff --git a/Makefile.target b/Makefile.target > index ff105c1..4f42582 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) > LIBS += $(CONFIG_VNC_TLS_LIBS) > endif > > +# xen backend driver support > +XEN_OBJS := xen-machine.o > +ifeq ($(CONFIG_XEN), yes) > + OBJS += $(XEN_OBJS) > + LIBS += $(XEN_LIBS) > + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes > +endif > + > # SCSI layer > OBJS+= lsi53c895a.o esp.o > > diff --git a/configure b/configure > index fd04766..34516b9 100755 > --- a/configure > +++ b/configure > @@ -108,6 +108,7 @@ uname_release="" > curses="yes" > nptl="yes" > mixemu="no" > +xen="no" > > # OS specific > targetos=`uname -s` > @@ -202,6 +203,7 @@ linux="yes" > linux_user="yes" > if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then > kqemu="yes" > + xen="yes" > audio_possible_drivers="$audio_possible_drivers fmod" > fi > ;; > @@ -285,6 +287,8 @@ for opt do > ;; > --disable-kqemu) kqemu="no" > ;; > + --disable-xen) xen="no" > + ;; > --disable-brlapi) brlapi="no" > ;; > --enable-profiler) profiler="yes" > @@ -421,6 +425,7 @@ echo " Available drivers: $audio_possible_drivers" > echo " --audio-card-list=LIST set list of additional emulated audio cards" > echo " Available cards: ac97 adlib cs4231a gus" > echo " --enable-mixemu enable mixer emulation" > +echo " --disable-xen disable xen backend driver support" > echo " --disable-brlapi disable BrlAPI" > echo " --disable-vnc-tls disable TLS encryption for VNC server" > echo " --disable-curses disable curses output" > @@ -681,6 +686,22 @@ else > fi > > ########################################## > +# xen probe > + > +if test "$xen" = "yes" ; then > +cat > $TMPC <<EOF > +#include <xs.h> > +#include <xenctrl.h> > +int main(void) { xs_daemon_open; xc_interface_open; } > +EOF > + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then > + : > + else > + xen="no" > + fi > +fi > + > +########################################## > # SDL probe > > sdl_too_old=no > @@ -916,6 +937,7 @@ if test -n "$sparc_cpu"; then > echo "Target Sparc Arch $sparc_cpu" > fi > echo "kqemu support $kqemu" > +echo "xen support $xen" > echo "brlapi support $brlapi" > echo "Documentation $build_docs" > [ ! -z "$uname_release" ] && \ > @@ -1167,6 +1189,11 @@ if test "$brlapi" = "yes" ; then > echo "#define CONFIG_BRLAPI 1" >> $config_h > echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak > fi > +if test "$xen" = "yes" ; then > + echo "CONFIG_XEN=yes" >> $config_mak > + echo "#define CONFIG_XEN 1" >> $config_h > + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak > +fi > > # XXX: suppress that > if [ "$bsd" = "yes" ] ; then > diff --git a/hw/boards.h b/hw/boards.h > index 22ac332..5931720 100644 > --- a/hw/boards.h > +++ b/hw/boards.h > @@ -29,6 +29,10 @@ extern QEMUMachine bareetraxfs_machine; > extern QEMUMachine pc_machine; > extern QEMUMachine isapc_machine; > > +/* xen_machine.c */ > +extern QEMUMachine xenpv_machine; > +extern QEMUMachine xenfv_machine; >Why does xenfv need its own machine type?> /* ppc.c */ > extern QEMUMachine prep_machine; > extern QEMUMachine core99_machine; > diff --git a/hw/xen-machine.c b/hw/xen-machine.c > new file mode 100644 > index 0000000..88f0f6e > --- /dev/null > +++ b/hw/xen-machine.c > @@ -0,0 +1,83 @@ > +/* > + * QEMU Xen PV Machine > + * > + * Copyright (c) 2007,08 Red Hat > + * > + * Permission is hereby granted, free of charge, to any person obtaining a copy > + * of this software and associated documentation files (the "Software"), to deal > + * in the Software without restriction, including without limitation the rights > + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > + * copies of the Software, and to permit persons to whom the Software is > + * furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice shall be included in > + * all copies or substantial portions of the Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > + * THE SOFTWARE. > + */ > + > +#include "hw.h" > +#include "boards.h" > + > +#include "xen.h" > + > +/* -------------------------------------------------------------------- */ > +/* variables */ > + > +int xen_domid; > + > +/* -------------------------------------------------------------------- */ > +/* paravirtualized xen guests */ > + > +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, > + const char *boot_device, DisplayState *ds, > + const char *kernel_filename, > + const char *kernel_cmdline, > + const char *initrd_filename, > + const char *cpu_model) > +{ > + CPUState *env; > + > + /* create dummy cpu, halted */ > + if (cpu_model == NULL) { > +#ifdef TARGET_X86_64 > + cpu_model = "qemu64"; > +#else > + cpu_model = "qemu32"; > +#endif > + } > + env = cpu_init(cpu_model); > + env->halted = 1; > +} > + > +QEMUMachine xenpv_machine = { > + "xenpv", > + "paravirtualized Xen machine", > + xenpv_init, > +}; > + > +/* -------------------------------------------------------------------- */ > +/* fully virtualized xen guests */ > + > +static void xenfv_init(ram_addr_t ram_size, int vga_ram_size, > + const char *boot_device, DisplayState *ds, > + const char *kernel_filename, > + const char *kernel_cmdline, > + const char *initrd_filename, > + const char *cpu_model) > +{ > + /* to be done */ > + fprintf(stderr, "%s: not implemented yet\n", __FUNCTION__); > +} > + > +QEMUMachine xenfv_machine = { > + "xenfv", > + "fully virtualized Xen machine", > + xenfv_init, > +}; > diff --git a/hw/xen.h b/hw/xen.h > new file mode 100644 > index 0000000..19349b5 > --- /dev/null > +++ b/hw/xen.h > @@ -0,0 +1,12 @@ > +/* > + * public xen header > + * stuff needed outside xen-*.c, i.e. interfaces to qemu. > + * should not depend on any xen headers being present in > + * /usr/include/xen, so it can be included unconditionally. > + * > + * internal bits for the xen backend drivers are in xen-backend.h > + */ > + > +/* xen-machine.c */ > +extern int xen_domid; > + > diff --git a/target-i386/machine.c b/target-i386/machine.c > index 91dbd55..98ece17 100644 > --- a/target-i386/machine.c > +++ b/target-i386/machine.c > @@ -9,6 +9,9 @@ void register_machines(void) > { > qemu_register_machine(&pc_machine); > qemu_register_machine(&isapc_machine); > +#ifdef CONFIG_XEN > + qemu_register_machine(&xenpv_machine); > +#endif > } > > static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) > diff --git a/vl.c b/vl.c > index 8801615..8aef3bd 100644 > --- a/vl.c > +++ b/vl.c > @@ -29,6 +29,7 @@ > #include "hw/audiodev.h" > #include "hw/isa.h" > #include "hw/baum.h" > +#include "hw/xen.h" > #include "net.h" > #include "console.h" > #include "sysemu.h" > @@ -7737,6 +7738,9 @@ static void help(int exitcode) > "-startdate select initial date of the clock\n" > "-icount [N|auto]\n" > " Enable virtual instruction counter with 2^N clock ticks per instruction\n" > +#ifdef CONFIG_XEN > + "-domid specify xen guest domain id\n" > +#endif > "\n" > "During emulation, the following keys are useful:\n" > "ctrl-alt-f toggle full screen\n" > @@ -7842,6 +7846,9 @@ enum { > QEMU_OPTION_startdate, > QEMU_OPTION_tb_size, > QEMU_OPTION_icount, > +#ifdef CONFIG_XEN > + QEMU_OPTION_domid, > +#endif > }; > > typedef struct QEMUOption { > @@ -7930,6 +7937,9 @@ const QEMUOption qemu_options[] = { > #ifdef CONFIG_CURSES > { "curses", 0, QEMU_OPTION_curses }, > #endif > +#ifdef CONFIG_XEN > + { "domid", HAS_ARG, QEMU_OPTION_domid }, > +#endif > > /* temporary options */ > { "usb", 0, QEMU_OPTION_usb }, > @@ -8150,7 +8160,7 @@ int main(int argc, char **argv) > #endif > uint32_t boot_devices_bitmap = 0; > int i; > - int snapshot, linux_boot, net_boot; > + int snapshot, linux_boot, net_boot, nodisk_ok; > const char *initrd_filename; > const char *kernel_filename, *kernel_cmdline; > const char *boot_devices = ""; > @@ -8787,6 +8797,11 @@ int main(int argc, char **argv) > icount_time_shift = strtol(optarg, NULL, 0); > } > break; > +#ifdef CONFIG_XEN > + case QEMU_OPTION_domid: > + xen_domid = atoi(optarg); > + break; > +#endif > } > } > } > @@ -8852,9 +8867,13 @@ int main(int argc, char **argv) > linux_boot = (kernel_filename != NULL); > net_boot = (boot_devices_bitmap >> (''n'' - ''a'')) & 0xF; > > - /* XXX: this should not be: some embedded targets just have flash */ > + /* need a disk for this machine to boot ? */ > + /* XXX: add embedded targets which just have flash */ > + nodisk_ok = 0; > + if (0 == strcmp(machine->name, "xenpv")) > + nodisk_ok = 1; > if (!linux_boot && net_boot == 0 && > - nb_drives_opt == 0) > + !nodisk_ok && nb_drives_opt == 0) > help(1);This patch is pretty clean with the exception of this bit. I think a cleaner way to do this would be to let the machine types specify whether a disk is needed or not. Regards, Anthony LIguori> > if (!linux_boot && *kernel_cmdline != ''\0'') { >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:13 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 2/7] xen: backend driver core
Gerd Hoffmann wrote:> This patch adds infrastructure for xen backend drivers living in qemu, > so drivers don''t need to implement common stuff on their own. It''s > mostly xenbus management stuff: some functions to access xentore, > setting up xenstore watches, callbacks on device discovery and state > changes, handle event channel, ... > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 2 +- > hw/list.h | 169 ++++++++++++++ > hw/xen-backend.c | 663 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen-backend.h | 116 ++++++++++ > hw/xen-machine.c | 5 +- > 5 files changed, 953 insertions(+), 2 deletions(-) > create mode 100644 hw/list.h > create mode 100644 hw/xen-backend.c > create mode 100644 hw/xen-backend.h > > diff --git a/Makefile.target b/Makefile.target > index 4f42582..0451048 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) > endif > > # xen backend driver support > -XEN_OBJS := xen-machine.o > +XEN_OBJS := xen-machine.o xen-backend.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/list.h b/hw/list.h > new file mode 100644 > index 0000000..fa9f790 > --- /dev/null > +++ b/hw/list.h >There''s already a list implementation in audio/sys-queue.h> diff --git a/hw/xen-backend.c b/hw/xen-backend.c > new file mode 100644 > index 0000000..4819624 > --- /dev/null > +++ b/hw/xen-backend.c > @@ -0,0 +1,663 @@ > +/* > + * xen backend driver infrastructure > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; under version 2 of the License. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <fcntl.h> > +#include <inttypes.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <sys/signal.h> > + > +#include <xs.h> > +#include <xenctrl.h> > +#include <xen/grant_table.h> > + > +#include "hw.h" > +#include "qemu-char.h" > +#include "xen-backend.h" > + > +/* ------------------------------------------------------------- */ > + > +/* public */ > +int xen_xc; > +struct xs_handle *xenstore = NULL; > + > +/* private */ > +static LIST_HEAD(xendevs); > +static const char *root = "/local/domain/0/backend"; > +static int debug = 0; > + > +/* ------------------------------------------------------------- */ >I think it would be good to provide some more commenting in this file. Just a high level description of XenStore and how all of this stuff fits together would be nice.> +int xenstore_write_str(const char *base, const char *node, const char *val) > +{ > + char abspath[BUFSIZE]; > + > + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); > + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) > + return -1; > + return 0; > +} > + > +char *xenstore_read_str(const char *base, const char *node) > +{ > + char abspath[BUFSIZE]; > + unsigned int len; > + > + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); > + return xs_read(xenstore, 0, abspath, &len); > +} > + > +int xenstore_write_int(const char *base, const char *node, int ival) > +{ > + char val[32]; > + > + snprintf(val, sizeof(val), "%d", ival); > + return xenstore_write_str(base, node, val); > +} > + > +int xenstore_read_int(const char *base, const char *node, int *ival) > +{ > + char *val; > + int rc = -1; > + > + val = xenstore_read_str(base, node); > + if (val && 1 == sscanf(val, "%d", ival)) > + rc = 0; > + free(val); > + return rc; > +} > + > +int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val) > +{ > + return xenstore_write_str(xendev->be, node, val); > +} > + > +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival) > +{ > + return xenstore_write_int(xendev->be, node, ival); > +} > + > +char *xenstore_read_be_str(struct xendev *xendev, const char *node) > +{ > + return xenstore_read_str(xendev->be, node); > +} > + > +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival) > +{ > + return xenstore_read_int(xendev->be, node, ival); > +} > + > +char *xenstore_read_fe_str(struct xendev *xendev, const char *node) > +{ > + return xenstore_read_str(xendev->fe, node); > +} > + > +int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival) > +{ > + return xenstore_read_int(xendev->fe, node, ival); > +} > + > +/* ------------------------------------------------------------- */ > + > +const char *xenbus_strstate(enum xenbus_state state) > +{ > + static const char *const name[] = { > + [ XenbusStateUnknown ] = "Unknown", > + [ XenbusStateInitialising ] = "Initialising", > + [ XenbusStateInitWait ] = "InitWait", > + [ XenbusStateInitialised ] = "Initialised", > + [ XenbusStateConnected ] = "Connected", > + [ XenbusStateClosing ] = "Closing", > + [ XenbusStateClosed ] = "Closed", > + }; > + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; > +} > + > +int xen_be_set_state(struct xendev *xendev, enum xenbus_state state) > +{ > + int rc; > + > + rc = xenstore_write_be_int(xendev, "state", state); > + if (0 != rc) >It''s a nit, but try to avoid the defensive ifs.> + return rc; > + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", > + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); > + xendev->be_state = state; > + return 0; > +} > + > +/* ------------------------------------------------------------- */ > + > +struct xendev *xen_be_find_xendev(char *type, int dom, int dev) > +{ > + struct xendev *xendev; > + struct list_head *item; > + > + list_for_each(item, &xendevs) { > + xendev = list_entry(item, struct xendev, next); > + if (xendev->dom != dom) > + continue; > + if (xendev->dev != dev) > + continue; > + if (0 != strcmp(xendev->type, type)) > + continue; > + return xendev; > + } > + return NULL; > +} > + > +/* > + * get xen backend device, allocate a new one if it doesn''t exist. > + */ > +static struct xendev *xen_be_get_xendev(char *type, int dom, int dev, > + struct devops *ops) > +{ > + struct xendev *xendev; > + > + xendev = xen_be_find_xendev(type, dom, dev); > + if (xendev) > + return xendev; > + > + /* init new xendev */ > + xendev = malloc(ops->size); > + memset(xendev,0,ops->size); >qemu_mallocz()> + xendev->type = type; > + xendev->dom = dom; > + xendev->dev = dev; > + xendev->ops = ops; > + snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d", > + root, xendev->type, xendev->dom, xendev->dev); > + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", > + xendev->type, xendev->dev); > + > + xendev->debug = debug; > + xendev->local_port = -1; > + > + xendev->evtchndev = xc_evtchn_open(); > + if (xendev->evtchndev < 0) { > + fprintf(stderr, "can''t open evtchn device\n"); > + free(xendev); >qemu_free() Considering all you''re doing, the patch looks pretty nice. Regards, Anthony Liguori> + return NULL; > + } > + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); > + > + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { > + xendev->gnttabdev = xc_gnttab_open(); > + if (xendev->gnttabdev < 0) { > + fprintf(stderr, "can''t open gnttab device\n"); > + xc_evtchn_close(xendev->evtchndev); > + free(xendev); > + return NULL; > + } > + } else { > + xendev->gnttabdev = -1; > + } > + > + list_add_tail(&xendev->next, &xendevs); > + > + if (xendev->ops->alloc) > + xendev->ops->alloc(xendev); > + > + return xendev; > +} > + > +/* > + * release xen backend device. > + */ > +static struct xendev *xen_be_del_xendev(int dom, int dev) > +{ > + struct xendev *xendev; > + struct list_head *item, *tmp; > + > + list_for_each_safe(item, tmp, &xendevs) { > + xendev = list_entry(item, struct xendev, next); > + if (xendev->dom != dom) > + continue; > + if (xendev->dev != dev && dev != -1) > + continue; > + > + if (xendev->ops->free) > + xendev->ops->free(xendev); > + > + if (xendev->fe) { > + char token[BUFSIZE]; > + snprintf(token, sizeof(token), "fe:%p", xendev); > + xs_unwatch(xenstore, xendev->fe, token); > + free(xendev->fe); > + } > + > + if (xendev->evtchndev >= 0) > + xc_evtchn_close(xendev->evtchndev); > + if (xendev->gnttabdev >= 0) > + xc_gnttab_close(xendev->gnttabdev); > + > + list_del(&xendev->next); > + free(xendev); > + } > + return NULL; > +} > + > +/* > + * Sync internal data structures on xenstore updates. > + * Node specifies the changed field. node = NULL means > + * update all fields (used for initialization). > + */ > +static void xen_be_backend_changed(struct xendev *xendev, const char *node) > +{ > + if (NULL == node || 0 == strcmp(node, "online")) { > + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) > + xendev->online = 0; > + } > + > + if (node) { > + xen_be_printf(xendev, 2, "backend update: %s\n", node); > + if (xendev->ops->backend_changed) > + xendev->ops->backend_changed(xendev, node); > + } > +} > + > +static void xen_be_frontend_changed(struct xendev *xendev, const char *node) > +{ > + int fe_state; > + > + if (NULL == node || 0 == strcmp(node, "state")) { > + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) > + fe_state = XenbusStateUnknown; > + if (xendev->fe_state != fe_state) > + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", > + xenbus_strstate(xendev->fe_state), > + xenbus_strstate(fe_state)); > + xendev->fe_state = fe_state; > + } > + if (NULL == node || 0 == strcmp(node, "protocol")) { > + free(xendev->protocol); > + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); > + if (xendev->protocol) > + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); > + } > + > + if (node) { > + xen_be_printf(xendev, 2, "frontend update: %s\n", node); > + if (xendev->ops->frontend_changed) > + xendev->ops->frontend_changed(xendev, node); > + } > +} > + > +/* ------------------------------------------------------------- */ > +/* Check for possible state transitions and perform them. */ > + > +/* > + * Initial xendev setup. Read frontend path, register watch for it. > + * Should succeed once xend finished setting up the backend device. > + * > + * Also sets initial state (-> Initializing) when done. Which > + * only affects the xendev->be_state variable as xenbus should > + * already be put into that state by xend. > + */ > +static int xen_be_try_setup(struct xendev *xendev) > +{ > + char token[BUFSIZE]; > + int be_state; > + > + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { > + xen_be_printf(xendev, 0, "reading backend state failed\n"); > + return -1; > + } > + > + if (be_state != XenbusStateInitialising) { > + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", > + xenbus_strstate(be_state)); > + return -1; > + } > + > + xendev->fe = xenstore_read_be_str(xendev, "frontend"); > + if (NULL == xendev->fe) { > + xen_be_printf(xendev, 0, "reading frontend path failed\n"); > + return -1; > + } > + > + /* setup frontend watch */ > + snprintf(token, sizeof(token), "fe:%p", xendev); > + if (!xs_watch(xenstore, xendev->fe, token)) { > + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", > + xendev->fe); > + return -1; > + } > + xen_be_set_state(xendev, XenbusStateInitialising); > + > + xen_be_backend_changed(xendev, NULL); > + xen_be_frontend_changed(xendev, NULL); > + return 0; > +} > + > +/* > + * Try initialize xendev. Prepare everything the backend can do > + * without synchronizing with the frontend. Fakes hotplug-status. No > + * hotplug involved here because this is about userspace drivers, thus > + * there are kernel backend devices which could invoke hotplug. > + * > + * Goes to InitWait on success. > + */ > +static int xen_be_try_init(struct xendev *xendev) > +{ > + int rc = 0; > + > + if (!xendev->online) { > + xen_be_printf(xendev, 1, "not online\n"); > + return -1; > + } > + > + if (xendev->ops->init) > + rc = xendev->ops->init(xendev); > + if (0 != rc) { > + xen_be_printf(xendev, 1, "init() failed\n"); > + return rc; > + } > + > + xenstore_write_be_str(xendev, "hotplug-status", "connected"); > + xen_be_set_state(xendev, XenbusStateInitWait); > + return 0; > +} > + > +/* > + * Try to connect xendev. Depends on the frontend being ready > + * for it (shared ring and evtchn info in xenstore, state being > + * Initialised or Connected). > + * > + * Goes to Connected on success. > + */ > +static int xen_be_try_connect(struct xendev *xendev) > +{ > + int rc = 0; > + > + if (xendev->fe_state != XenbusStateInitialised && > + xendev->fe_state != XenbusStateConnected) { > + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { > + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); > + } else { > + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); > + return -1; > + } > + } > + > + if (xendev->ops->connect) > + rc = xendev->ops->connect(xendev); > + if (0 != rc) { > + xen_be_printf(xendev, 0, "connect() failed\n"); > + return rc; > + } > + > + xen_be_set_state(xendev, XenbusStateConnected); > + > +#if 0 > + if (xendev->ops->event) > + xendev->ops->event(xendev); > +#endif > + return 0; > +} > + > +/* > + * Teardown connection. > + * > + * Goes to Closed when done. > + */ > +static void xen_be_disconnect(struct xendev *xendev) > +{ > + if (xendev->be_state == XenbusStateClosed) > + return; > + if (xendev->ops->disconnect) > + xendev->ops->disconnect(xendev); > + xen_be_set_state(xendev, XenbusStateClosed); > +} > + > +/* > + * state change dispatcher function > + */ > +void xen_be_check_state(struct xendev *xendev) > +{ > + int rc = 0; > + > + /* frontend may request shutdown from almost anywhere */ > + if (xendev->fe_state == XenbusStateClosing || > + xendev->fe_state == XenbusStateClosed) { > + xen_be_disconnect(xendev); > + return; > + } > + > + /* check for possible backend state transitions */ > + for (;;) { > + switch (xendev->be_state) { > + case XenbusStateUnknown: > + rc = xen_be_try_setup(xendev); > + break; > + case XenbusStateInitialising: > + rc = xen_be_try_init(xendev); > + break; > + case XenbusStateInitWait: > + rc = xen_be_try_connect(xendev); > + break; > + default: > + rc = -1; > + } > + if (0 != rc) > + break; > + } > +} > + > +/* ------------------------------------------------------------- */ > + > +static int xenstore_scan(char *type, int dom, struct devops *ops) > +{ > + struct xendev *xendev; > + char path[BUFSIZE], token[BUFSIZE]; > + char **dev = NULL; > + unsigned int cdev, j; > + > + /* setup watch */ > + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); > + snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); > + if (!xs_watch(xenstore, path, token)) { > + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); > + return -1; > + } > + > + /* look for backends */ > + dev = xs_directory(xenstore, 0, path, &cdev); > + if (!dev) > + return 0; > + for (j = 0; j < cdev; j++) { > + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); > + if (NULL == xendev) > + continue; > + xen_be_check_state(xendev); > + } > + free(dev); > + return 0; > +} > + > +static void xenstore_update_be(char *watch, char *type, int dom, > + struct devops *ops) > +{ > + struct xendev *xendev; > + char path[BUFSIZE]; > + unsigned int len, dev; > + > + len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); > + if (0 != strncmp(path, watch, len)) > + return; > + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { > + strcpy(path, ""); > + if (1 != sscanf(watch+len, "/%u", &dev)) > + dev = -1; > + } > + if (-1 == dev) > + return; > + > + if (0) { > + /* FIXME: detect devices being deleted from xenstore ... */ > + xen_be_del_xendev(dom, dev); > + } > + > + xendev = xen_be_get_xendev(type, dom, dev, ops); > + if (NULL != xendev) { > + xen_be_backend_changed(xendev, path); > + xen_be_check_state(xendev); > + } > +} > + > +static void xenstore_update_fe(char *watch, struct xendev *xendev) > +{ > + char *node; > + unsigned int len; > + > + len = strlen(xendev->fe); > + if (0 != strncmp(xendev->fe, watch, len)) > + return; > + if (watch[len] != ''/'') > + return; > + node = watch + len + 1; > + > + xen_be_frontend_changed(xendev, node); > + xen_be_check_state(xendev); > +} > + > +static void xenstore_update(void *unused) > +{ > + char **vec = NULL; > + intptr_t type, ops, ptr; > + unsigned int dom, count; > + > + vec = xs_read_watch(xenstore, &count); > + if (NULL == vec) > + goto cleanup; > + > + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, > + &type, &dom, &ops)) > + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); > + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) > + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); > + > +cleanup: > + free(vec); > +} > + > +static void xen_be_evtchn_event(void *opaque) > +{ > + struct xendev *xendev = opaque; > + evtchn_port_t port; > + > + port = xc_evtchn_pending(xendev->evtchndev); > + if (port != xendev->local_port) { > + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", > + port, xendev->local_port); > + return; > + } > + xc_evtchn_unmask(xendev->evtchndev, port); > + > + if (xendev->ops->event) > + xendev->ops->event(xendev); > +} > + > +/* -------------------------------------------------------------------- */ > + > +int xen_be_init(void) > +{ > + xenstore = xs_daemon_open(); > + if (!xenstore) { > + fprintf(stderr, "can''t connect to xenstored\n"); > + return -1; > + } > + > + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) > + goto err; > + > + xen_xc = xc_interface_open(); > + if (-1 == xen_xc) { > + fprintf(stderr, "can''t open xen interface\n"); > + goto err; > + } > + return 0; > + > +err: > + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); > + xs_daemon_close(xenstore); > + xenstore = NULL; > + > + return -1; > +} > + > +int xen_be_register(char *type, struct devops *ops) > +{ > + return xenstore_scan(type, xen_domid, ops); > +} > + > +int xen_be_bind_evtchn(struct xendev *xendev) > +{ > + if (xendev->local_port != -1) > + return 0; > + xendev->local_port = xc_evtchn_bind_interdomain > + (xendev->evtchndev, xendev->dom, xendev->remote_port); > + if (-1 == xendev->local_port) { > + xendev->local_port = 0; > + return -1; > + } > + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), > + xen_be_evtchn_event, NULL, xendev); > +#if 0 > + xc_evtchn_unmask(xendev->evtchndev, xendev->local_port); > +#endif > + return 0; > +} > + > +void xen_be_unbind_evtchn(struct xendev *xendev) > +{ > + if (xendev->local_port == -1) > + return; > + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); > + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); > + xendev->local_port = -1; > +} > + > +int xen_be_send_notify(struct xendev *xendev) > +{ > + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); > +} > + > +/* > + * msg_level: > + * 0 == errors. > + * 1 == informative debug messages. > + * 2 == noisy debug messages. > + * 3 == will flood your log. > + */ > +void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) > +{ > + va_list args; > + > + if (msg_level > xendev->debug) > + return; > + fprintf(stderr, "xen be: %s: ", xendev->name); > + va_start(args, fmt); > + vfprintf(stderr, fmt, args); > + va_end(args); > +} > diff --git a/hw/xen-backend.h b/hw/xen-backend.h > new file mode 100644 > index 0000000..db36ae7 > --- /dev/null > +++ b/hw/xen-backend.h > @@ -0,0 +1,116 @@ > +#include <stddef.h> > +#include <xs.h> > +#include <xenctrl.h> > +#include <xen/elfnote.h> > +#include <xen/elfstructs.h> > + > +#include <xen/io/xenbus.h> > + > +#include "list.h" > + > +#include "hw.h" > +#include "xen.h" > + > +/* > + * tweaks needed to build with different xen versions > + * 0x00030205 -> 3.1.0 > + * 0x00030207 -> 3.2.0 > + * 0x00030208 -> unstable > + */ > +#include <xen/xen-compat.h> > +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 > +# define xc_map_foreign_pages xc_map_foreign_batch > +#endif > +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 > +# define xen_mb() mb() > +# define xen_rmb() rmb() > +# define xen_wmb() wmb() > +#endif > + > +/* ------------------------------------------------------------- */ > + > +#define container_of(ptr, type, member) ({ \ > + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ > + (type *)( (char *)__mptr - offsetof(type,member) );}) > + > +/* ------------------------------------------------------------- */ > + > +#define BUFSIZE 1024 > + > +struct xendev; > + > +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ > +#define DEVOPS_FLAG_NEED_GNTDEV 1 > +/* don''t expect frontend doing correct state transitions (aka console quirk) */ > +#define DEVOPS_FLAG_IGNORE_STATE 2 > + > +struct devops { > + size_t size; > + uint32_t flags; > + void (*alloc)(struct xendev *xendev); > + int (*init)(struct xendev *xendev); > + int (*connect)(struct xendev *xendev); > + void (*event)(struct xendev *xendev); > + void (*disconnect)(struct xendev *xendev); > + int (*free)(struct xendev *xendev); > + void (*backend_changed)(struct xendev *xendev, const char *node); > + void (*frontend_changed)(struct xendev *xendev, const char *node); > +}; > + > +struct xendev { > + char *type; > + int dom; > + int dev; > + char name[64]; > + int debug; > + > + enum xenbus_state be_state; > + enum xenbus_state fe_state; > + int online; > + char be[BUFSIZE]; > + char *fe; > + char *protocol; > + int remote_port; > + int local_port; > + > + int evtchndev; > + int gnttabdev; > + > + struct devops *ops; > + struct list_head next; > +}; > + > +/* ------------------------------------------------------------- */ > + > +/* variables */ > +extern int xen_domid; /* set by cmd line option, in vl.c */ > +extern int xen_xc; > +extern struct xs_handle *xenstore; > + > +/* xenstore helper functions */ > +int xenstore_write_str(const char *base, const char *node, const char *val); > +int xenstore_write_int(const char *base, const char *node, int ival); > +char *xenstore_read_str(const char *base, const char *node); > +int xenstore_read_int(const char *base, const char *node, int *ival); > + > +int xenstore_write_be_str(struct xendev *xendev, const char *node, const char *val); > +int xenstore_write_be_int(struct xendev *xendev, const char *node, int ival); > +char *xenstore_read_be_str(struct xendev *xendev, const char *node); > +int xenstore_read_be_int(struct xendev *xendev, const char *node, int *ival); > +char *xenstore_read_fe_str(struct xendev *xendev, const char *node); > +int xenstore_read_fe_int(struct xendev *xendev, const char *node, int *ival); > + > +const char *xenbus_strstate(enum xenbus_state state); > +struct xendev *xen_be_find_xendev(char *type, int dom, int dev); > +void xen_be_check_state(struct xendev *xendev); > + > +/* xen backend driver bits */ > +int xen_be_init(void); > +int xen_be_register(char *type, struct devops *ops); > +int xen_be_set_state(struct xendev *xendev, enum xenbus_state state); > +int xen_be_bind_evtchn(struct xendev *xendev); > +void xen_be_unbind_evtchn(struct xendev *xendev); > +int xen_be_send_notify(struct xendev *xendev); > +void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) > + __attribute__ ((format(printf, 3, 4))); > + > diff --git a/hw/xen-machine.c b/hw/xen-machine.c > index 88f0f6e..798c0a7 100644 > --- a/hw/xen-machine.c > +++ b/hw/xen-machine.c > @@ -25,7 +25,7 @@ > #include "hw.h" > #include "boards.h" > > -#include "xen.h" > +#include "xen-backend.h" > > /* -------------------------------------------------------------------- */ > /* variables */ > @@ -54,6 +54,9 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, > } > env = cpu_init(cpu_model); > env->halted = 1; > + > + /* setup xen backend handlers */ > + xen_be_init(); > } > > QEMUMachine xenpv_machine = { >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:17 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
Gerd Hoffmann wrote:> This patch adds a xenconsole backend driver. It it based on current > xen-unstable code. It has been changed to make use of the common > backend driver code. > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 1 + > hw/xen-backend.h | 3 + > hw/xen-console.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen-machine.c | 2 + > 4 files changed, 277 insertions(+), 0 deletions(-) > create mode 100644 hw/xen-console.c > > diff --git a/Makefile.target b/Makefile.target > index 0451048..05619fa 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -517,6 +517,7 @@ endif > > # xen backend driver support > XEN_OBJS := xen-machine.o xen-backend.o > +XEN_OBJS += xen-console.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/xen-backend.h b/hw/xen-backend.h > index db36ae7..55ffd31 100644 > --- a/hw/xen-backend.h > +++ b/hw/xen-backend.h > @@ -114,3 +114,6 @@ int xen_be_send_notify(struct xendev *xendev); > void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) > __attribute__ ((format(printf, 3, 4))); > > +/* actual backend drivers */ > +struct devops xen_console_ops; /* xen_console.c */ > + > diff --git a/hw/xen-console.c b/hw/xen-console.c > new file mode 100644 > index 0000000..9c67f1b > --- /dev/null > +++ b/hw/xen-console.c > @@ -0,0 +1,271 @@ > +/* > + * Copyright (C) International Business Machines Corp., 2005 > + * Author(s): Anthony Liguori <aliguori@us.ibm.com> > + * > + * Copyright (C) Red Hat 2007 > + * > + * Xen Console > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; under version 2 of the License. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <malloc.h> > +#include <stdlib.h> > +#include <errno.h> > +#include <string.h> > +#include <sys/select.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <termios.h> > +#include <stdarg.h> > +#include <sys/mman.h> > +#include <xs.h> > +#include <xen/io/console.h> > +#include <xenctrl.h> > + > +#include "hw.h" > +#include "sysemu.h" > +#include "qemu-char.h" > +#include "xen-backend.h" > + > +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) > + > +struct buffer { > + uint8_t *data; > + size_t consumed; > + size_t size; > + size_t capacity; > + size_t max_capacity; > +}; > + > +struct xen_console { > + struct xendev xendev; /* must be first */ > + struct buffer buffer; > + char console[BUFSIZE]; > + int ring_ref; > + void *sring; > + CharDriverState *chr; > + int backlog; > +}; > + > +static void buffer_append(struct xen_console *con) > +{ > + struct buffer *buffer = &con->buffer; > + XENCONS_RING_IDX cons, prod, size; > + struct xencons_interface *intf = con->sring; > + > + cons = intf->out_cons; > + prod = intf->out_prod; > + xen_mb(); > + > + size = prod - cons; > + if ((size == 0) || (size > sizeof(intf->out))) > + return; > + > + if ((buffer->capacity - buffer->size) < size) { > + buffer->capacity += (size + 1024); > + buffer->data = realloc(buffer->data, buffer->capacity); > + if (buffer->data == NULL) { > + dolog(LOG_ERR, "Memory allocation failed"); > + exit(ENOMEM); > + } > + } > + > + while (cons != prod) > + buffer->data[buffer->size++] = intf->out[ > + MASK_XENCONS_IDX(cons++, intf->out)]; > + > + xen_mb(); > + intf->out_cons = cons; > + xen_be_send_notify(&con->xendev); > + > + if (buffer->max_capacity && > + buffer->size > buffer->max_capacity) { > + /* Discard the middle of the data. */ > + > + size_t over = buffer->size - buffer->max_capacity; > + uint8_t *maxpos = buffer->data + buffer->max_capacity; > + > + memmove(maxpos - over, maxpos, over); > + buffer->data = realloc(buffer->data, buffer->max_capacity); > + buffer->size = buffer->capacity = buffer->max_capacity; > + > + if (buffer->consumed > buffer->max_capacity - over) > + buffer->consumed = buffer->max_capacity - over; > + } > +} > + > +static void buffer_advance(struct buffer *buffer, size_t len) > +{ > + buffer->consumed += len; > + if (buffer->consumed == buffer->size) { > + buffer->consumed = 0; > + buffer->size = 0; > + } > +} > + > +static int ring_free_bytes(struct xen_console *con) > +{ > + struct xencons_interface *intf = con->sring; > + XENCONS_RING_IDX cons, prod, space; > + > + cons = intf->in_cons; > + prod = intf->in_prod; > + xen_mb(); > + > + space = prod - cons; > + if (space > sizeof(intf->in)) > + return 0; /* ring is screwed: ignore it */ > + > + return (sizeof(intf->in) - space); > +} > + > +static int xencons_can_receive(void *opaque) > +{ > + struct xen_console *con = opaque; > + return ring_free_bytes(con); > +} > + > +static void xencons_receive(void *opaque, const uint8_t *buf, int len) > +{ > + struct xen_console *con = opaque; > + struct xencons_interface *intf = con->sring; > + XENCONS_RING_IDX prod; > + int i, max; > + > + max = ring_free_bytes(con); > + /* The can_receive() func limits this, but check again anyway */ > + if (max < len) > + len = max; > + > + prod = intf->in_prod; > + for (i = 0; i < len; i++) { > + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] > + buf[i]; > + } > + xen_wmb(); > + intf->in_prod = prod; > + xen_be_send_notify(&con->xendev); > +} > + > +static void xencons_send(struct xen_console *con) > +{ > + ssize_t len, size; > + > + size = con->buffer.size - con->buffer.consumed; > + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, > + size); > + if (len < 1) { > + if (!con->backlog) { > + con->backlog = 1; > + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); > + } > + } else { > + buffer_advance(&con->buffer, len); > + if (con->backlog && len == size) { > + con->backlog = 0; > + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); > + } > + } > +} > + > +/* -------------------------------------------------------------------- */ > + > +static int con_init(struct xendev *xendev) > +{ > + struct xen_console *con = container_of(xendev, struct xen_console, xendev); > + char *type; > + > + if (!serial_hds[con->xendev.dev]) { > + xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev); > + return -1; > + } > + > + /* setup */ > + snprintf(con->console, sizeof(con->console), > + "/local/domain/%d/console", con->xendev.dom); > + con->chr = serial_hds[con->xendev.dev]; > + > + type = xenstore_read_str(con->console, "type"); > + if (!type || 0 != strcmp(type, "ioemu")) { > + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); > + return -1; > + } > + > + return 0; > +} > + > +static int con_connect(struct xendev *xendev) > +{ > + struct xen_console *con = container_of(xendev, struct xen_console, xendev); > + int limit; > + > + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) > + return -1; > + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) > + return -1; > + if (0 == xenstore_read_int(con->console, "limit", &limit)) > + con->buffer.max_capacity = limit; > + > + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, > + XC_PAGE_SIZE, > + PROT_READ|PROT_WRITE, > + con->ring_ref); > + if (!con->sring) > + return -1; > + > + xen_be_bind_evtchn(&con->xendev); > + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, > + NULL, con); > + > + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", > + con->ring_ref, > + con->xendev.remote_port, > + con->xendev.local_port, > + con->buffer.max_capacity); > + return 0; > +} > + > +static void con_disconnect(struct xendev *xendev) > +{ > + struct xen_console *con = container_of(xendev, struct xen_console, xendev); > + > + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); > + xen_be_unbind_evtchn(&con->xendev); > + > + if (con->sring) { > + munmap(con->sring, XC_PAGE_SIZE); > + con->sring = NULL; > + } > +} > + > +static void con_event(struct xendev *xendev) > +{ > + struct xen_console *con = container_of(xendev, struct xen_console, xendev); > + > + buffer_append(con); > + if (con->buffer.size - con->buffer.consumed) > + xencons_send(con); > +} > + > +/* -------------------------------------------------------------------- */ > + > +struct devops xen_console_ops = { >I missed it in the previous patchset, but it would be nice to QEMU-ify these things. For instance, ''struct xen_console'' => ''XenConsole'', ''struct devopts'' => ''XenDevOpts'', etc. Regards, Anthony Liguori> + .size = sizeof(struct xen_console), > + .flags = DEVOPS_FLAG_IGNORE_STATE, > + .init = con_init, > + .connect = con_connect, > + .event = con_event, > + .disconnect = con_disconnect, > +}; > diff --git a/hw/xen-machine.c b/hw/xen-machine.c > index 798c0a7..da10982 100644 > --- a/hw/xen-machine.c > +++ b/hw/xen-machine.c > @@ -57,6 +57,8 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, > > /* setup xen backend handlers */ > xen_be_init(); > + > + xen_be_register("console", &xen_console_ops); > } > > QEMUMachine xenpv_machine = { >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:22 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Gerd Hoffmann wrote:> This patch adds a frsamebuffer (and kbd+mouse) backend driver. It > it based on current xen-unstable code. It has been changed to make > use of the common backend driver code. It also has been changed to > compile with xen headers older than unstable (aka son-to-be 3.3). > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 2 +- > hw/xen-backend.h | 4 + > hw/xen-framebuffer.c | 925 ++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen-machine.c | 5 + > 4 files changed, 935 insertions(+), 1 deletions(-) > create mode 100644 hw/xen-framebuffer.c > > diff --git a/Makefile.target b/Makefile.target > index 05619fa..66d41ee 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -517,7 +517,7 @@ endif > > # xen backend driver support > XEN_OBJS := xen-machine.o xen-backend.o > -XEN_OBJS += xen-console.o > +XEN_OBJS += xen-console.o xen-framebuffer.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/xen-backend.h b/hw/xen-backend.h > index 55ffd31..3facf90 100644 > --- a/hw/xen-backend.h > +++ b/hw/xen-backend.h > @@ -116,4 +116,8 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) > > /* actual backend drivers */ > struct devops xen_console_ops; /* xen_console.c */ > +struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ > +struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ > + > +void xen_set_display(int domid, DisplayState *ds); > > diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c > new file mode 100644 > index 0000000..716eafd > --- /dev/null > +++ b/hw/xen-framebuffer.c > @@ -0,0 +1,925 @@ > +/* > + * xen paravirt framebuffer backend > >No copyright?> + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; under version 2 of the License. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <stdarg.h> > +#include <stdlib.h> > +#include <sys/types.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <stdbool.h> > +#include <sys/mman.h> > +#include <errno.h> > +#include <stdio.h> > +#include <string.h> > +#include <time.h> > + > +#include <xs.h> > +#include <xenctrl.h> > +#include <xen/event_channel.h> > +#include <xen/io/xenbus.h> > +#include <xen/io/fbif.h> > +#include <xen/io/kbdif.h> > +#include <xen/io/protocols.h> > + > +#include "hw.h" > +#include "sysemu.h" > +#include "console.h" > +#include "qemu-char.h" > +#include "xen-backend.h" > + > +#ifndef BTN_LEFT > +#define BTN_LEFT 0x110 /* from <linux/input.h> */ > +#endif > + > +/* -------------------------------------------------------------------- */ > + > +struct xen_common { > + struct xendev xendev; /* must be first */ > + void *page; > + DisplayState *ds; > +}; > + > +struct xen_input { > + struct xen_common c; > + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ > + int button_state; /* Last seen pointer button state */ > + int extended; > + QEMUPutMouseEntry *qmouse; > +}; > + > +#define UP_QUEUE 8 > + > +struct xen_fb { > + struct xen_common c; > + size_t fb_len; > + int row_stride; > + int depth; > + int width; > + int height; > + int offset; > + void *pixels; > + int feature_update; > + int refresh_period; > + int bug_trigger; > + > + struct { > + int x,y,w,h; > + } up_rects[UP_QUEUE]; > + int up_count; > + int up_fullscreen; > +}; > + > +/* -------------------------------------------------------------------- */ > + > +static int common_bind(struct xen_common *c) > +{ > + int mfn; > + > + if (-1 == xenstore_read_fe_int(&c->xendev, "page-ref", &mfn)) > + return -1; > + if (-1 == xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port)) > + return -1; > + > + xen_be_bind_evtchn(&c->xendev); > + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", > + mfn, c->xendev.remote_port, c->xendev.local_port); > + > + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, > + XC_PAGE_SIZE, > + PROT_READ | PROT_WRITE, mfn); > + if (NULL == c->page) > + return -1; > + return 0; > +} > + > +static void common_unbind(struct xen_common *c) > +{ > + if (c->page) { > + munmap(c->page, XC_PAGE_SIZE); > + c->page = NULL; > + } > + xen_be_unbind_evtchn(&c->xendev); > +} > + > +/* -------------------------------------------------------------------- */ > + > +/* > + * Tables to map from scancode to Linux input layer keycode. > + * Scancodes are hardware-specific. These maps assumes a > + * standard AT or PS/2 keyboard which is what QEMU feeds us. > + */ > +const unsigned char atkbd_set2_keycode[512] = { > + > + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, > + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, > + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, > + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, > + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, > + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, > + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, > + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, > + > + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, > + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, > + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, > + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, > + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, > + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, > + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, > + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, > + > +}; > + > +const unsigned char atkbd_unxlate_table[128] = { > + > + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, > + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, > + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, > + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, > + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, > + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, > + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, > + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 > + > +}; > + > +static unsigned char scancode2linux[512]; > + > +/* Send an event to the keyboard frontend driver */ > +static int xenfb_kbd_event(struct xen_input *xenfb, > + union xenkbd_in_event *event) > +{ > + struct xenkbd_page *page = xenfb->c.page; > + uint32_t prod; > + > + if (xenfb->c.xendev.be_state != XenbusStateConnected) > + return 0; > + > + prod = page->in_prod; > + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { > + errno = EAGAIN; > + return -1; > + } > + > + xen_mb(); /* ensure ring space available */ > + XENKBD_IN_RING_REF(page, prod) = *event; > + xen_wmb(); /* ensure ring contents visible */ > + page->in_prod = prod + 1; > + return xen_be_send_notify(&xenfb->c.xendev); > +} > + > +/* Send a keyboard (or mouse button) event */ > +static int xenfb_send_key(struct xen_input *xenfb, bool down, int keycode) > +{ > + union xenkbd_in_event event; > + > + memset(&event, 0, XENKBD_IN_EVENT_SIZE); > + event.type = XENKBD_TYPE_KEY; > + event.key.pressed = down ? 1 : 0; > + event.key.keycode = keycode; > + > + return xenfb_kbd_event(xenfb, &event); > +} > + > +/* Send a relative mouse movement event */ > +static int xenfb_send_motion(struct xen_input *xenfb, > + int rel_x, int rel_y, int rel_z) > +{ > + union xenkbd_in_event event; > + > + memset(&event, 0, XENKBD_IN_EVENT_SIZE); > + event.type = XENKBD_TYPE_MOTION; > + event.motion.rel_x = rel_x; > + event.motion.rel_y = rel_y; > +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 > + event.motion.rel_z = rel_z; > +#endif > + > + return xenfb_kbd_event(xenfb, &event); > +} > + > +/* Send an absolute mouse movement event */ > +static int xenfb_send_position(struct xen_input *xenfb, > + int abs_x, int abs_y, int rel_z) > +{ > + union xenkbd_in_event event; > + > + memset(&event, 0, XENKBD_IN_EVENT_SIZE); > + event.type = XENKBD_TYPE_POS; > + event.pos.abs_x = abs_x; > + event.pos.abs_y = abs_y; > +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 > + event.pos.rel_z = rel_z; > +#endif > + > + return xenfb_kbd_event(xenfb, &event); > +} > + > +/* > + * Send a key event from the client to the guest OS > + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. > + * We have to turn this into a Linux Input layer keycode. > + * > + * Extra complexity from the fact that with extended scancodes > + * (like those produced by arrow keys) this method gets called > + * twice, but we only want to send a single event. So we have to > + * track the ''0xe0'' scancode state & collapse the extended keys > + * as needed. > + * > + * Wish we could just send scancodes straight to the guest which > + * already has code for dealing with this... > + */ > +static void xenfb_key_event(void *opaque, int scancode) > +{ > + struct xen_input *xenfb = opaque; > + int down = 1; > + > + if (scancode == 0xe0) { > + xenfb->extended = 1; > + return; > + } else if (scancode & 0x80) { > + scancode &= 0x7f; > + down = 0; > + } > + if (xenfb->extended) { > + scancode |= 0x80; > + xenfb->extended = 0; > + } > + xenfb_send_key(xenfb, down, scancode2linux[scancode]); > +} > + > +/* > + * Send a mouse event from the client to the guest OS > + * > + * The QEMU mouse can be in either relative, or absolute mode. > + * Movement is sent separately from button state, which has to > + * be encoded as virtual key events. We also don''t actually get > + * given any button up/down events, so have to track changes in > + * the button state. > + */ > +static void xenfb_mouse_event(void *opaque, > + int dx, int dy, int dz, int button_state) > +{ > + struct xen_input *xenfb = opaque; > + int i; > + > + if (xenfb->abs_pointer_wanted) > + xenfb_send_position(xenfb, > + dx * (xenfb->c.ds->width - 1) / 0x7fff, > + dy * (xenfb->c.ds->height - 1) / 0x7fff, > + dz); > + else > + xenfb_send_motion(xenfb, dx, dy, dz); > + > + for (i = 0 ; i < 8 ; i++) { > + int lastDown = xenfb->button_state & (1 << i); > + int down = button_state & (1 << i); > + if (down == lastDown) > + continue; > + > + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) > + return; > + } > + xenfb->button_state = button_state; > +} > + > +static int input_init(struct xendev *xendev) > +{ > + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); > + static int first = 1; > + int i; > + > + if (first) { > + /* Prepare scancode mapping table */ > + for (i = 0; i < 128; i++) { > + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; > + scancode2linux[i | 0x80] > + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; > + } > + first = 0; > + } > + > + if (!in->c.ds) { > + /* xen_set_display() below will set that and trigger us again */ > + xen_be_printf(xendev, 1, "ds not set (yet)\n"); > + return -1; > + } > + > + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); > + return 0; > +} > + > +static int input_connect(struct xendev *xendev) > +{ > + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); > + int rc; > + > + if (-1 == xenstore_read_fe_int(xendev, "request-abs-pointer", &in->abs_pointer_wanted)) > + in->abs_pointer_wanted = 0; > + > + rc = common_bind(&in->c); > + if (0 != rc) > + return rc; > + > + qemu_add_kbd_event_handler(xenfb_key_event, in); > + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, > + in->abs_pointer_wanted, > + "Xen PVFB Mouse"); > + return 0; > +} > + > +static void input_disconnect(struct xendev *xendev) > +{ > + struct xen_input *in = container_of(xendev, struct xen_input, c.xendev); > + > + if (in->qmouse) { > + qemu_remove_mouse_event_handler(in->qmouse); > + in->qmouse = NULL; > + } > + qemu_add_kbd_event_handler(NULL, NULL); > + common_unbind(&in->c); > +} > + > +static void input_event(struct xendev *xendev) > +{ > + struct xen_input *xenfb = container_of(xendev, struct xen_input, c.xendev); > + struct xenkbd_page *page = xenfb->c.page; > + > + /* We don''t understand any keyboard events, so just ignore them. */ > + if (page->out_prod == page->out_cons) > + return; > + page->out_cons = page->out_prod; > + xen_be_send_notify(&xenfb->c.xendev); > +} > + > +/* -------------------------------------------------------------------- */ > + > +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) > +{ > + uint32_t *src32 = src; > + uint64_t *src64 = src; > + int i; > + > + for (i = 0; i < count; i++) > + dst[i] = (mode == 32) ? src32[i] : src64[i]; > +} > + > +static int xenfb_map_fb(struct xen_fb *xenfb) > +{ > + struct xenfb_page *page = xenfb->c.page; > + char *protocol = xenfb->c.xendev.protocol; > + int n_fbmfns; > + int n_fbdirs; > + unsigned long *pgmfns = NULL; > + unsigned long *fbmfns = NULL; > + void *map, *pd; > + int mode, ret = -1; > + > + /* default to native */ > + pd = page->pd; > + mode = sizeof(unsigned long) * 8; > + > + if (!protocol) { > + /* > + * Undefined protocol, some guesswork needed. > + * > + * Old frontends which don''t set the protocol use > + * one page directory only, thus pd[1] must be zero. > + * pd[1] of the 32bit struct layout and the lower > + * 32 bits of pd[0] of the 64bit struct layout have > + * the same location, so we can check that ... > + */ > + uint32_t *ptr32 = NULL; > + uint32_t *ptr64 = NULL; > +#if defined(__i386__) > + ptr32 = (void*)page->pd; > + ptr64 = ((void*)page->pd) + 4; > +#elif defined(__x86_64__) > + ptr32 = ((void*)page->pd) - 4; > + ptr64 = (void*)page->pd; > +#endif > + if (ptr32) { > + if (0 == ptr32[1]) { > + mode = 32; > + pd = ptr32; > + } else { > + mode = 64; > + pd = ptr64; > + } > + } > +#if defined(__x86_64__) > + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) { > + /* 64bit dom0, 32bit domU */ > + mode = 32; > + pd = ((void*)page->pd) - 4; > +#elif defined(__i386__) > + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) { > + /* 32bit dom0, 64bit domU */ > + mode = 64; > + pd = ((void*)page->pd) + 4; > +#endif > + } > + > + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; > + n_fbdirs = n_fbmfns * mode / 8; > + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; > + > + pgmfns = malloc(sizeof(unsigned long) * n_fbdirs); > + fbmfns = malloc(sizeof(unsigned long) * n_fbmfns); > + if (!pgmfns || !fbmfns) > + goto out; > + > + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); > + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, > + PROT_READ, pgmfns, n_fbdirs); > + if (map == NULL) > + goto out; > + xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); > + munmap(map, n_fbdirs * XC_PAGE_SIZE); > + > + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, > + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); > + if (xenfb->pixels == NULL) > + goto out; > + > + ret = 0; /* all is fine */ > + > +out: > + if (pgmfns) > + free(pgmfns); > + if (fbmfns) > + free(fbmfns); > + return ret; > +} > + > +static int xenfb_configure_fb(struct xen_fb *xenfb, size_t fb_len_lim, > + int width, int height, int depth, > + size_t fb_len, int offset, int row_stride) > +{ > + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); > + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; > + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; > + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; > + int max_width, max_height; > + > + if (fb_len_lim > fb_len_max) { > + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", > + fb_len_lim, fb_len_max); > + fb_len_lim = fb_len_max; > + } > + if (fb_len_lim && fb_len > fb_len_lim) { > + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", > + fb_len, fb_len_lim); > + fb_len = fb_len_lim; > + } > + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { > + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", > + depth); > + return -1; > + } > + if (row_stride < 0 || row_stride > fb_len) { > + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); > + return -1; > + } > + max_width = row_stride / (depth / 8); > + if (width < 0 || width > max_width) { > + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", > + width, max_width); > + width = max_width; > + } > + if (offset < 0 || offset >= fb_len) { > + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", > + offset, fb_len - 1); > + return -1; > + } > + max_height = (fb_len - offset) / row_stride; > + if (height < 0 || height > max_height) { > + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", > + height, max_height); > + height = max_height; > + } > + xenfb->fb_len = fb_len; > + xenfb->row_stride = row_stride; > + xenfb->depth = depth; > + xenfb->width = width; > + xenfb->height = height; > + xenfb->offset = offset; > + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", > + width, height, depth, offset, row_stride); > + return 0; > +} > + > +/* A convenient function for munging pixels between different depths */ > +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ > + for (line = y ; line < (y+h) ; line++) { \ > + SRC_T *src = (SRC_T *)(xenfb->pixels \ > + + xenfb->offset \ > + + (line * xenfb->row_stride) \ > + + (x * xenfb->depth / 8)); \ > + DST_T *dst = (DST_T *)(xenfb->c.ds->data \ > + + (line * xenfb->c.ds->linesize) \ > + + (x * xenfb->c.ds->depth / 8)); \ > + int col; \ > + const int RSS = 32 - (RSB + GSB + BSB); \ > + const int GSS = 32 - (GSB + BSB); \ > + const int BSS = 32 - (BSB); \ > + const uint32_t RSM = (~0U) << (32 - RSB); \ > + const uint32_t GSM = (~0U) << (32 - GSB); \ > + const uint32_t BSM = (~0U) << (32 - BSB); \ > + const int RDS = 32 - (RDB + GDB + BDB); \ > + const int GDS = 32 - (GDB + BDB); \ > + const int BDS = 32 - (BDB); \ > + const uint32_t RDM = (~0U) << (32 - RDB); \ > + const uint32_t GDM = (~0U) << (32 - GDB); \ > + const uint32_t BDM = (~0U) << (32 - BDB); \ > + for (col = x ; col < (x+w) ; col++) { \ > + uint32_t spix = *src; \ > + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ > + (((spix << GSS) & GSM & GDM) >> GDS) | \ > + (((spix << BSS) & BSM & BDM) >> BDS); \ > + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ > + dst = (DST_T *) ((unsigned long) dst + xenfb->c.ds->depth / 8); \ > + } \ > + } >There are similar conversion macros in the VGA code. I don''t know how practical it would be to reuse them but it''s at least worth looking at. Regards, Anthony Liguori> + > +/* This copies data from the guest framebuffer region, into QEMU''s copy > + * NB. QEMU''s copy is stored in the pixel format of a) the local X > + * server (SDL case) or b) the current VNC client pixel format. > + * When shifting between colour depths we preserve the MSB. > + */ > +static void xenfb_guest_copy(struct xen_fb *xenfb, int x, int y, int w, int h) > +{ > + int line; > + > + if (1 /* !xenfb->c.ds->shared_buf */) { > + if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */ > + for (line = y ; line < (y+h) ; line++) { > + memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8), > + xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8), > + w * xenfb->depth / 8); > + } > + } else { /* Mismatch requires slow pixel munging */ > + /* 8 bit == r:3 g:3 b:2 */ > + /* 16 bit == r:5 g:6 b:5 */ > + /* 24 bit == r:8 g:8 b:8 */ > + /* 32 bit == r:8 g:8 b:8 (padding:8) */ > + if (xenfb->depth == 8) { > + if (xenfb->c.ds->depth == 16) { > + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); > + } else if (xenfb->c.ds->depth == 32) { > + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); > + } > + } else if (xenfb->depth == 16) { > + if (xenfb->c.ds->depth == 8) { > + BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2); > + } else if (xenfb->c.ds->depth == 32) { > + BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8); > + } > + } else if (xenfb->depth == 24 || xenfb->depth == 32) { > + if (xenfb->c.ds->depth == 8) { > + BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2); > + } else if (xenfb->c.ds->depth == 16) { > + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); > + } else if (xenfb->c.ds->depth == 32) { > + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); > + } > + } > + } > + } > + dpy_update(xenfb->c.ds, x, y, w, h); > +} > + > +#ifdef XENFB_TYPE_REFRESH_PERIOD > +static int xenfb_queue_full(struct xen_fb *xenfb) > +{ > + struct xenfb_page *page = xenfb->c.page; > + uint32_t cons, prod; > + > + prod = page->in_prod; > + cons = page->in_cons; > + return prod - cons == XENFB_IN_RING_LEN; > +} > + > +static void xenfb_send_event(struct xen_fb *xenfb, union xenfb_in_event *event) > +{ > + uint32_t prod; > + struct xenfb_page *page = xenfb->c.page; > + > + prod = page->in_prod; > + /* caller ensures !xenfb_queue_full() */ > + xen_mb(); /* ensure ring space available */ > + XENFB_IN_RING_REF(page, prod) = *event; > + xen_wmb(); /* ensure ring contents visible */ > + page->in_prod = prod + 1; > + > + xen_be_send_notify(&xenfb->c.xendev); > +} > + > +static void xenfb_send_refresh_period(struct xen_fb *xenfb, int period) > +{ > + union xenfb_in_event event; > + > + memset(&event, 0, sizeof(event)); > + event.type = XENFB_TYPE_REFRESH_PERIOD; > + event.refresh_period.period = period; > + xenfb_send_event(xenfb, &event); > +} > +#endif > + > +/* > + * Periodic update of display. > + * Also transmit the refresh interval to the frontend. > + * > + * Never ever do any qemu display operations > + * (resize, screen update) outside this function. > + * Our screen might be inactive. When asked for > + * an update we know it is active. > + */ > +static void xenfb_update(void *opaque) > +{ > + struct xen_fb *xenfb = opaque; > + int i; > + > + if (xenfb->feature_update) { > +#ifdef XENFB_TYPE_REFRESH_PERIOD > + int period; > + > + if (xenfb_queue_full(xenfb)) > + return; > + > + /* > + * TODO: xen''s qemu-dm seems to have some patches to > + * make the qemu display code avoid unneeded > + * work. > + * - Port them over. > + * - Put ds->idle back into use then. > + * - Same goes for ds->shared_buf btw. > + */ > + if (0 /* xenfb->c.ds->idle */) > + period = XENFB_NO_REFRESH; > + else { > + period = xenfb->c.ds->gui_timer_interval; > + if (!period) > + period = 30; // GUI_REFRESH_INTERVAL (see vl.c) > + } > + > + if (xenfb->refresh_period != period) { > + dprintf("%s: %d\n", __FUNCTION__, period); > + xenfb_send_refresh_period(xenfb, period); > + xenfb->refresh_period = period; > + } > +#else > + ; /* nothing */ > +#endif > + } else { > + /* we don''t get update notifications, thus use the > + * sledge hammer approach ... */ > + xenfb->up_fullscreen = 1; > + } > + > + /* resize if needed */ > + if (xenfb->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) { > + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n"); > + dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height); > + xenfb->up_fullscreen = 1; > + } > + > + /* run queued updates */ > + if (xenfb->up_fullscreen) { > + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); > + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); > + } else if (xenfb->up_count) { > + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); > + for (i = 0; i < xenfb->up_count; i++) > + xenfb_guest_copy(xenfb, > + xenfb->up_rects[i].x, > + xenfb->up_rects[i].y, > + xenfb->up_rects[i].w, > + xenfb->up_rects[i].h); > + } else { > + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); > + } > + xenfb->up_count = 0; > + xenfb->up_fullscreen = 0; > +} > + > +static void xenfb_handle_events(struct xen_fb *xenfb) > +{ > + uint32_t prod, cons; > + struct xenfb_page *page = xenfb->c.page; > + > + prod = page->out_prod; > + if (prod == page->out_cons) > + return; > + xen_rmb(); /* ensure we see ring contents up to prod */ > + for (cons = page->out_cons; cons != prod; cons++) { > + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); > + int x, y, w, h; > + > + switch (event->type) { > + case XENFB_TYPE_UPDATE: > + if (xenfb->up_count == UP_QUEUE) > + xenfb->up_fullscreen = 1; > + if (xenfb->up_fullscreen) > + break; > + x = MAX(event->update.x, 0); > + y = MAX(event->update.y, 0); > + w = MIN(event->update.width, xenfb->width - x); > + h = MIN(event->update.height, xenfb->height - y); > + if (w < 0 || h < 0) { > + fprintf(stderr, "xen be: %s: bogus update ignored\n", > + xenfb->c.xendev.name); > + break; > + } > + if (x != event->update.x || y != event->update.y > + || w != event->update.width > + || h != event->update.height) { > + fprintf(stderr, "xen be: %s: bogus update clipped\n", > + xenfb->c.xendev.name); > + } > + if (w == xenfb->width && h > xenfb->height / 2) { > + /* scroll detector: updated more than 50% of the lines, > + * don''t bother keeping track of the rectangles then */ > + xenfb->up_fullscreen = 1; > + } else { > + xenfb->up_rects[xenfb->up_count].x = x; > + xenfb->up_rects[xenfb->up_count].y = y; > + xenfb->up_rects[xenfb->up_count].w = w; > + xenfb->up_rects[xenfb->up_count].h = h; > + xenfb->up_count++; > + } > + break; > +#ifdef XENFB_TYPE_RESIZE > + case XENFB_TYPE_RESIZE: > + if (xenfb_configure_fb(xenfb, xenfb->fb_len, > + event->resize.width, > + event->resize.height, > + event->resize.depth, > + xenfb->fb_len, > + event->resize.offset, > + event->resize.stride) < 0) > + break; > + xenfb_invalidate(xenfb); > + break; > +#endif > + } > + } > + xen_mb(); /* ensure we''re done with ring contents */ > + page->out_cons = cons; > +} > + > +/* QEMU display state changed, so refresh the framebuffer copy */ > +static void xenfb_invalidate(void *opaque) > +{ > + struct xen_fb *xenfb = opaque; > + xenfb->up_fullscreen = 1; > +} > + > +static int fb_init(struct xendev *xendev) > +{ > + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); > + > + if (!fb->c.ds) { > + /* xen_set_display() below will set that and trigger us again */ > + xen_be_printf(xendev, 1, "ds not set (yet)\n"); > + return -1; > + } > + > + fb->refresh_period = -1; > + > +#ifdef XENFB_TYPE_RESIZE > + xenstore_write_be_int(xendev, "feature-resize", 1); > +#endif > + return 0; > +} > + > +static int fb_connect(struct xendev *xendev) > +{ > + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); > + struct xenfb_page *fb_page; > + int videoram; > + int rc; > + > + if (-1 == xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update)) > + fb->feature_update = 0; > + if (fb->feature_update) > + xenstore_write_be_int(xendev, "request-update", 1); > + > + if (-1 == xenstore_read_fe_int(xendev, "videoram", &videoram)) > + videoram = 0; > + > + rc = common_bind(&fb->c); > + if (0 != rc) > + return rc; > + > + fb_page = fb->c.page; > + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, > + fb_page->width, fb_page->height, fb_page->depth, > + fb_page->mem_length, 0, fb_page->line_length); > + if (0 != rc) > + return rc; > + > + rc = xenfb_map_fb(fb); > + if (0 != rc) > + return rc; > + > + graphic_console_init(fb->c.ds, > + xenfb_update, > + xenfb_invalidate, > + NULL, > + NULL, > + fb); > + > + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", > + fb->feature_update, videoram); > + > + return 0; > +} > + > +static void fb_disconnect(struct xendev *xendev) > +{ > + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); > + > + /* FIXME: Hmm, un-init gfx display? can qemu handle that? */ > + common_unbind(&fb->c); > +} > + > +static void fb_frontend_changed(struct xendev *xendev, const char *node) > +{ > + struct xen_fb *fb = container_of(xendev, struct xen_fb, c.xendev); > + > + /* > + * Set state to Connected *again* once the frontend switched > + * to connected. We must trigger the watch a second time to > + * workaround a frontend bug. > + */ > + if (0 == fb->bug_trigger && 0 == strcmp(node, "state") && > + xendev->fe_state == XenbusStateConnected && > + xendev->be_state == XenbusStateConnected) { > + xen_be_set_state(xendev, XenbusStateConnected); > + fb->bug_trigger = 1; /* only once */ > + } > +} > + > +static void fb_event(struct xendev *xendev) > +{ > + struct xen_fb *xenfb = container_of(xendev, struct xen_fb, c.xendev); > + > + xenfb_handle_events(xenfb); > + xen_be_send_notify(&xenfb->c.xendev); > +} > + > +/* -------------------------------------------------------------------- */ > + > +struct devops xen_kbdmouse_ops = { > + .size = sizeof(struct xen_input), > + .init = input_init, > + .connect = input_connect, > + .disconnect = input_disconnect, > + .event = input_event, > +}; > + > +struct devops xen_framebuffer_ops = { > + .size = sizeof(struct xen_fb), > + .init = fb_init, > + .connect = fb_connect, > + .disconnect = fb_disconnect, > + .event = fb_event, > + .frontend_changed = fb_frontend_changed, > +}; > + > +static void xen_set_display_type(int domid, char *type, DisplayState *ds) > +{ > + struct xendev *xendev; > + struct xen_common *c; > + > + xendev = xen_be_find_xendev(type, domid, 0); > + if (!xendev) > + return; > + c = container_of(xendev, struct xen_common, xendev); > + c->ds = ds; > + xen_be_printf(xendev, 1, "ds is %p\n", ds); > + /* retry ->init() */ > + xen_be_check_state(xendev); > +} > + > +void xen_set_display(int domid, DisplayState *ds) > +{ > + xen_set_display_type(domid, "vkbd", ds); > + xen_set_display_type(domid, "vfb", ds); > +} > diff --git a/hw/xen-machine.c b/hw/xen-machine.c > index da10982..c03cb53 100644 > --- a/hw/xen-machine.c > +++ b/hw/xen-machine.c > @@ -59,6 +59,11 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, > xen_be_init(); > > xen_be_register("console", &xen_console_ops); > + xen_be_register("vkbd", &xen_kbdmouse_ops); > + xen_be_register("vfb", &xen_framebuffer_ops); > + > + /* setup framebuffer */ > + xen_set_display(xen_domid, ds); > } > > QEMUMachine xenpv_machine = { >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:25 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Gerd Hoffmann wrote:> This patch adds a block device backend driver to qemu. It is a pure > userspace implemention using the gntdev interface. It uses "qdisk" as > backend name in xenstore so it doesn''t interfere with the other existing > backends (blkback aka "vbd" and tapdisk aka "tap"). > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 2 +- > hw/xen-backend.h | 2 + > hw/xen-blkif.h | 103 ++++++++ > hw/xen-disk.c | 681 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > hw/xen-machine.c | 1 + > sysemu.h | 2 +- > vl.c | 4 + > 7 files changed, 793 insertions(+), 2 deletions(-) > create mode 100644 hw/xen-blkif.h > create mode 100644 hw/xen-disk.c > > diff --git a/Makefile.target b/Makefile.target > index 66d41ee..2d599d2 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -517,7 +517,7 @@ endif > > # xen backend driver support > XEN_OBJS := xen-machine.o xen-backend.o > -XEN_OBJS += xen-console.o xen-framebuffer.o > +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/xen-backend.h b/hw/xen-backend.h > index 3facf90..941b0a6 100644 > --- a/hw/xen-backend.h > +++ b/hw/xen-backend.h > @@ -10,6 +10,7 @@ > > #include "hw.h" > #include "xen.h" > +#include "sysemu.h" > > /* > * tweaks needed to build with different xen versions > @@ -118,6 +119,7 @@ void xen_be_printf(struct xendev *xendev, int msg_level, const char *fmt, ...) > struct devops xen_console_ops; /* xen_console.c */ > struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ > struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ > +struct devops xen_blkdev_ops; /* xen_disk.c */ > > void xen_set_display(int domid, DisplayState *ds); > > diff --git a/hw/xen-blkif.h b/hw/xen-blkif.h > new file mode 100644 > index 0000000..254a5fd > --- /dev/null > +++ b/hw/xen-blkif.h > @@ -0,0 +1,103 @@ > +#ifndef __XEN_BLKIF_H__ > +#define __XEN_BLKIF_H__ > + > +#include <xen/io/ring.h> > +#include <xen/io/blkif.h> > +#include <xen/io/protocols.h> > + > +/* Not a real protocol. Used to generate ring structs which contain > + * the elements common to all protocols only. This way we get a > + * compiler-checkable way to use common struct elements, so we can > + * avoid using switch(protocol) in a number of places. */ > +struct blkif_common_request { > + char dummy; > +}; > +struct blkif_common_response { > + char dummy; > +}; > + > +/* i386 protocol version */ > +#pragma pack(push, 4) > +struct blkif_x86_32_request { > + uint8_t operation; /* BLKIF_OP_??? */ > + uint8_t nr_segments; /* number of segments */ > + blkif_vdev_t handle; /* only for read/write requests */ > + uint64_t id; /* private guest value, echoed in resp */ > + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ > + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; > +}; > +struct blkif_x86_32_response { > + uint64_t id; /* copied from request */ > + uint8_t operation; /* copied from request */ > + int16_t status; /* BLKIF_RSP_??? */ > +}; > +typedef struct blkif_x86_32_request blkif_x86_32_request_t; > +typedef struct blkif_x86_32_response blkif_x86_32_response_t; > +#pragma pack(pop) > + > +/* x86_64 protocol version */ > +struct blkif_x86_64_request { > + uint8_t operation; /* BLKIF_OP_??? */ > + uint8_t nr_segments; /* number of segments */ > + blkif_vdev_t handle; /* only for read/write requests */ > + uint64_t __attribute__((__aligned__(8))) id; > + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ > + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; > +}; > +struct blkif_x86_64_response { > + uint64_t __attribute__((__aligned__(8))) id; > + uint8_t operation; /* copied from request */ > + int16_t status; /* BLKIF_RSP_??? */ > +}; > +typedef struct blkif_x86_64_request blkif_x86_64_request_t; > +typedef struct blkif_x86_64_response blkif_x86_64_response_t; > + > +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); > +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); > +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); > + > +union blkif_back_rings { > + blkif_back_ring_t native; > + blkif_common_back_ring_t common; > + blkif_x86_32_back_ring_t x86_32; > + blkif_x86_64_back_ring_t x86_64; > +}; > +typedef union blkif_back_rings blkif_back_rings_t; > + > +enum blkif_protocol { > + BLKIF_PROTOCOL_NATIVE = 1, > + BLKIF_PROTOCOL_X86_32 = 2, > + BLKIF_PROTOCOL_X86_64 = 3, > +}; > + > +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) > +{ > + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; > + > + dst->operation = src->operation; > + dst->nr_segments = src->nr_segments; > + dst->handle = src->handle; > + dst->id = src->id; > + dst->sector_number = src->sector_number; > + if (n > src->nr_segments) > + n = src->nr_segments; > + for (i = 0; i < n; i++) > + dst->seg[i] = src->seg[i]; > +} > + > +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) > +{ > + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; > + > + dst->operation = src->operation; > + dst->nr_segments = src->nr_segments; > + dst->handle = src->handle; > + dst->id = src->id; > + dst->sector_number = src->sector_number; > + if (n > src->nr_segments) > + n = src->nr_segments; > + for (i = 0; i < n; i++) > + dst->seg[i] = src->seg[i]; > +} > + > +#endif /* __XEN_BLKIF_H__ */ > diff --git a/hw/xen-disk.c b/hw/xen-disk.c > new file mode 100644 > index 0000000..6947363 > --- /dev/null > +++ b/hw/xen-disk.c > @@ -0,0 +1,681 @@ > +/* > + * xen paravirt block device backend > + * > + * FIXME: the code is designed to handle multiple outstanding > + * requests (using aio or using threads), which isn''t used right > + * now due to limitations of the qemu block driver interface. > + * > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; under version 2 of the License. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <signal.h> > +#include <inttypes.h> > +#include <time.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <pthread.h> >This doesn''t seem to be needed? Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 14:27 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 6/7] xen: add net backend driver.
Gerd Hoffmann wrote:> This patch adds a network interface backend driver to qemu. It is a pure > userspace implemention using the gntdev interface. It uses "qnet" as > backend name in xenstore so it doesn''t interfere with the netback > backend (aka "vnif"). > > The network backend is hooked into the corrosponding qemu vlan, i.e. > vif 0 is hooked into vlan 0. To make the packages actually arrive > somewhere you additionally have to link the vlan to the outside world > using the usual qemu command line options such as "-net tap,...". > > Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> > --- > Makefile.target | 2 +- > hw/xen-backend.h | 1 + > hw/xen-machine.c | 1 + > hw/xen-nic.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 451 insertions(+), 1 deletions(-) > create mode 100644 hw/xen-nic.c > > diff --git a/Makefile.target b/Makefile.target > index 2d599d2..281d7fa 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -517,7 +517,7 @@ endif > > # xen backend driver support > XEN_OBJS := xen-machine.o xen-backend.o > -XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o > +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o > ifeq ($(CONFIG_XEN), yes) > OBJS += $(XEN_OBJS) > LIBS += $(XEN_LIBS) > diff --git a/hw/xen-backend.h b/hw/xen-backend.h > index 941b0a6..2db2c3c 100644 > --- a/hw/xen-backend.h > +++ b/hw/xen-backend.h > @@ -120,6 +120,7 @@ struct devops xen_console_ops; /* xen_console.c */ > struct devops xen_kbdmouse_ops; /* xen_framebuffer.c */ > struct devops xen_framebuffer_ops; /* xen_framebuffer.c */ > struct devops xen_blkdev_ops; /* xen_disk.c */ > +struct devops xen_netdev_ops; /* xen_nic.c */ > > void xen_set_display(int domid, DisplayState *ds); > > diff --git a/hw/xen-machine.c b/hw/xen-machine.c > index 1b647a2..3fa4079 100644 > --- a/hw/xen-machine.c > +++ b/hw/xen-machine.c > @@ -62,6 +62,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, > xen_be_register("vkbd", &xen_kbdmouse_ops); > xen_be_register("vfb", &xen_framebuffer_ops); > xen_be_register("qdisk", &xen_blkdev_ops); > + xen_be_register("qnic", &xen_netdev_ops); > > /* setup framebuffer */ > xen_set_display(xen_domid, ds); > diff --git a/hw/xen-nic.c b/hw/xen-nic.c > new file mode 100644 > index 0000000..56d1474 > --- /dev/null > +++ b/hw/xen-nic.c > @@ -0,0 +1,448 @@ > +/* > + * xen paravirt network card backend > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; under version 2 of the License. > + * > + * This program 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 General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <signal.h> > +#include <inttypes.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <pthread.h> > +#include <sys/socket.h> > +#include <sys/ioctl.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <sys/wait.h> > +#include <linux/if.h> > +#include <linux/if_tun.h> > + > +#include <xs.h> > +#include <xenctrl.h> > +#include <xen/io/xenbus.h> > +#include <xen/io/netif.h> > + > +#include "hw.h" > +#include "net.h" > +#include "qemu-char.h" > +#include "xen-backend.h" > + > +/* ------------------------------------------------------------- */ > + > +struct netdev { > + struct xendev xendev; /* must be first */ > + char *mac; > + int tx_work; > + int tx_ring_ref; > + int rx_ring_ref; > + struct netif_tx_sring *txs; > + struct netif_rx_sring *rxs; > + netif_tx_back_ring_t tx_ring; > + netif_rx_back_ring_t rx_ring; > + VLANClientState *vs; > +}; > + > +/* ------------------------------------------------------------- */ > + > +#define PROTO_TCP 6 > +#define PROTO_UDP 17 > + > +static uint32_t checksum_add(int len, uint8_t *buf) > +{ > + uint32_t sum = 0; > + int i; > + > + for (i = 0; i < len; i++) { > + if (i & 1) > + sum += (uint32_t)buf[i]; > + else > + sum += (uint32_t)buf[i] << 8; > + } > + return sum; > +} > + > +static uint16_t checksum_finish(uint32_t sum) > +{ > + while (sum>>16) > + sum = (sum & 0xFFFF)+(sum >> 16); > + return ~sum; > +} > + > +static uint16_t checksum_tcpudp(uint16_t length, uint16_t proto, > + uint8_t *addrs, uint8_t *buf) > +{ > + uint32_t sum = 0; > + > + sum += checksum_add(length, buf); // payload > + sum += checksum_add(8, addrs); // src + dst address > + sum += proto + length; // protocol & length > + return checksum_finish(sum); > +} > + > +static void checksum_calculate(uint8_t *data, int length) > +{ > + int hlen, plen, proto, csum_offset; > + uint16_t csum; > + > + if ((data[14] & 0xf0) != 0x40) > + return; /* not IPv4 */ > + hlen = (data[14] & 0x0f) * 4; > + plen = (data[16] << 8 | data[17]) - hlen; > + proto = data[23]; > + > + switch (proto) { > + case PROTO_TCP: > + csum_offset = 16; > + break; > + case PROTO_UDP: > + csum_offset = 6; > + break; > + default: > + return; > + } > + > + if (plen < csum_offset+2) > + return; > + > + data[14+hlen+csum_offset] = 0; > + data[14+hlen+csum_offset+1] = 0; > + csum = checksum_tcpudp(plen, proto, data+14+12, data+14+hlen); > + data[14+hlen+csum_offset] = csum >> 8; > + data[14+hlen+csum_offset+1] = csum & 0xff; > +} > >The same thing is done in e1000, I believe. We''ll also probably want to be able to support this for virtio so this would be pretty good to make common code. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 14:52 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
>> +/* xen_machine.c */ >> +extern QEMUMachine xenpv_machine; >> +extern QEMUMachine xenfv_machine; > > Why does xenfv need its own machine type?This is how xen''s qemu-dm handles it at the moment and I''ve decided to do it the same way for simplicity. I think qemu could also figure it using a hypercall.>> - /* XXX: this should not be: some embedded targets just have flash */ >> + /* need a disk for this machine to boot ? */ >> + /* XXX: add embedded targets which just have flash */ >> + nodisk_ok = 0; >> + if (0 == strcmp(machine->name, "xenpv")) >> + nodisk_ok = 1; >> if (!linux_boot && net_boot == 0 && >> - nb_drives_opt == 0) >> + !nodisk_ok && nb_drives_opt == 0) >> help(1); > > This patch is pretty clean with the exception of this bit. I think a > cleaner way to do this would be to let the machine types specify whether > a disk is needed or not.Agreed, I''ll make it a machine_type field. thanks, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 15:43 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
>> + >> +struct devops xen_console_ops = { >> > > I missed it in the previous patchset, but it would be nice to QEMU-ify > these things. For instance, ''struct xen_console'' => ''XenConsole'', > ''struct devopts'' => ''XenDevOpts'', etc.Hmm, I will if I have to. But I don''t like that style very much and a quick grep through the qemu header files shows that the CapsStyle isn''t used consequently everythere. I see a happy style mix. The core structures tend to be CapsStyle though ... cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 15:45 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 6/7] xen: add net backend driver.
Anthony Liguori wrote:> Gerd Hoffmann wrote: >> +static void checksum_calculate(uint8_t *data, int length) >> +{ > > The same thing is done in e1000, I believe. We''ll also probably want to > be able to support this for virtio so this would be pretty good to make > common code.Good point, I''ll split it into a separate patch and source file. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-28 15:51 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 2/7] xen: backend driver core
Anthony Liguori wrote:>> --- /dev/null >> +++ b/hw/list.h >> > > There''s already a list implementation in audio/sys-queue.hWell hidden ;) Thanks for the pointer.> I think it would be good to provide some more commenting in this file. > Just a high level description of XenStore and how all of this stuff fits > together would be nice.Probably a good idea, yes. thanks, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-28 19:04 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
Gerd Hoffmann wrote:>>> + >>> +struct devops xen_console_ops = { >>> >>> >> I missed it in the previous patchset, but it would be nice to QEMU-ify >> these things. For instance, ''struct xen_console'' => ''XenConsole'', >> ''struct devopts'' => ''XenDevOpts'', etc. >> > > Hmm, I will if I have to. > > But I don''t like that style very much and a quick grep through the qemu > header files shows that the CapsStyle isn''t used consequently > everythere. I see a happy style mix. The core structures tend to be > CapsStyle though ... >I much prefer the Linux style myself, but I prefer consistency over any particular style. I think it would be good to modify the code to be consistent with the rest of the core QEMU structure. Regards, Anthony Liguori> cheers, > Gerd > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Jul-28 23:14 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit :> >+/* xen_machine.c */ > >+extern QEMUMachine xenpv_machine; > >+extern QEMUMachine xenfv_machine; > > Why does xenfv need its own machine type?IIRC that''s at least because it adds its own Xen platform -specific PCI card. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-29 07:38 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Samuel Thibault wrote:> Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit : >>> +/* xen_machine.c */ >>> +extern QEMUMachine xenpv_machine; >>> +extern QEMUMachine xenfv_machine; >> Why does xenfv need its own machine type? > > IIRC that''s at least because it adds its own Xen platform -specific PCI card.That is only one tiny bit of the differences between paravirtual and fully virtualized machines, there is plenty more. The question is whenever qemu should try figure itself whenever the virtual machine it serves is fully virtualized or whenever we''ll explicitly tell it qemu. I think it is better to explicitly tell qemu whenever we want create a paravirtual or a full virtualized machine. First, because this is how it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second, it leaves the door open to have qemu also create the domain. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Daniel P. Berrange
2008-Jul-29 08:10 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote:> >> +/* xen_machine.c */ > >> +extern QEMUMachine xenpv_machine; > >> +extern QEMUMachine xenfv_machine; > > > > Why does xenfv need its own machine type? > > This is how xen''s qemu-dm handles it at the moment and I''ve decided to > do it the same way for simplicity. I think qemu could also figure it > using a hypercall.This is something I originally added to QEMU in Xen''s tree. The basic idea is that the ''xenpv'' macjine is a machine which /only/ provides the paravirtualized Xen backends drivers. The ''xenfv'' machine type is just the same as the ''pc'' machine type, but with the /addition/ of the Xen paravirtualized backends. Perhaps the latter could be thought of as more of a ''xenpc'' (cf ''pc'' and ''isapc'') becasue its a variation on the regular PC machine type. Annoying that all the acronyms I come up with only differ by one letter :-) Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Daniel P. Berrange
2008-Jul-29 08:12 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
On Tue, Jul 29, 2008 at 09:38:01AM +0200, Gerd Hoffmann wrote:> Samuel Thibault wrote: > > Anthony Liguori, le Mon 28 Jul 2008 09:04:54 -0500, a écrit : > >>> +/* xen_machine.c */ > >>> +extern QEMUMachine xenpv_machine; > >>> +extern QEMUMachine xenfv_machine; > >> Why does xenfv need its own machine type? > > > > IIRC that''s at least because it adds its own Xen platform -specific PCI card. > > That is only one tiny bit of the differences between paravirtual and > fully virtualized machines, there is plenty more. The question is > whenever qemu should try figure itself whenever the virtual machine it > serves is fully virtualized or whenever we''ll explicitly tell it qemu. > > I think it is better to explicitly tell qemu whenever we want create a > paravirtual or a full virtualized machine. First, because this is how > it is handled right now in xen (via -M [ xenpv | xenfv ] ), and second, > it leaves the door open to have qemu also create the domain.The setup process for ''xenpv'' vs ''xenfv'' is really very different. As I mentioned in the other mail, ''xenfv'' is really much closer to ''pc'' machine type, then ''xenpv''. Thus I think its better to keep them explicitly separate - they call out to shared code where needed anyway, so there''s no serious code duplication problem there. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-29 08:55 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Daniel P. Berrange wrote:> The setup process for ''xenpv'' vs ''xenfv'' is really very different. As I > mentioned in the other mail, ''xenfv'' is really much closer to ''pc'' > machine type, then ''xenpv''.Yes. I think we will keep the xenpv machine type for sure. For full virtualization it might be more useful to just use the "pc" machine type (and maybe make xenfv an alias for compatibility).>From qemu''s point of view xen and kvm are not very different: no cpuemulation needed, some chips such as apic are handled by someone else too, some additional devices might be there (xen platform device, virtio devices). So it would make sense to handle them the same way, especially once we have the qemuaccel stuff in place which will add an abstraction layer for this. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-29 13:32 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Daniel P. Berrange wrote:> On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote: > >>>> +/* xen_machine.c */ >>>> +extern QEMUMachine xenpv_machine; >>>> +extern QEMUMachine xenfv_machine; >>>> >>> Why does xenfv need its own machine type? >>> >> This is how xen''s qemu-dm handles it at the moment and I''ve decided to >> do it the same way for simplicity. I think qemu could also figure it >> using a hypercall. >> > > This is something I originally added to QEMU in Xen''s tree. The basic > idea is that the ''xenpv'' macjine is a machine which /only/ provides the > paravirtualized Xen backends drivers. The ''xenfv'' machine type is just > the same as the ''pc'' machine type, but with the /addition/ of the Xen > paravirtualized backends. Perhaps the latter could be thought of as > more of a ''xenpc'' (cf ''pc'' and ''isapc'') becasue its a variation on the > regular PC machine type. Annoying that all the acronyms I come up with > only differ by one letter :-) >Why wouldn''t the Xen backends be added by appropriate -net or -drive options? For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen Regards, Anthony Liguori> Daniel >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Daniel P. Berrange
2008-Jul-29 14:24 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote:> Daniel P. Berrange wrote: > >On Mon, Jul 28, 2008 at 04:52:28PM +0200, Gerd Hoffmann wrote: > > > >>>>+/* xen_machine.c */ > >>>>+extern QEMUMachine xenpv_machine; > >>>>+extern QEMUMachine xenfv_machine; > >>>> > >>>Why does xenfv need its own machine type? > >>> > >>This is how xen''s qemu-dm handles it at the moment and I''ve decided to > >>do it the same way for simplicity. I think qemu could also figure it > >>using a hypercall. > >> > > > >This is something I originally added to QEMU in Xen''s tree. The basic > >idea is that the ''xenpv'' macjine is a machine which /only/ provides the > >paravirtualized Xen backends drivers. The ''xenfv'' machine type is just > >the same as the ''pc'' machine type, but with the /addition/ of the Xen > >paravirtualized backends. Perhaps the latter could be thought of as > >more of a ''xenpc'' (cf ''pc'' and ''isapc'') becasue its a variation on the > >regular PC machine type. Annoying that all the acronyms I come up with > >only differ by one letter :-) > > > > Why wouldn''t the Xen backends be added by appropriate -net or -drive > options? For instance, qemu -drive file=foo.img,if=xen -net nic,model=xenThat would work for certain setup tasks, but not all. In Xen''s branch of QEMU, the machine init function has to set various hypervisor parameters which aren''t directly associated with QEMU command line args, and initialize things like the map-cache for > 4GB guest support. In the Xennite work I had further stuff going on there too - more general initialization of Xenstore parameters - eg stuffing the name & uuid from QEMU''s argv into right place in xenstore. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-29 14:32 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Anthony Liguori wrote:> Daniel P. Berrange wrote: >> This is something I originally added to QEMU in Xen''s tree. The basic >> idea is that the ''xenpv'' macjine is a machine which /only/ provides the >> paravirtualized Xen backends drivers. The ''xenfv'' machine type is just >> the same as the ''pc'' machine type, but with the /addition/ of the Xen >> paravirtualized backends. Perhaps the latter could be thought of as >> more of a ''xenpc'' (cf ''pc'' and ''isapc'') becasue its a variation on the >> regular PC machine type. Annoying that all the acronyms I come up with >> only differ by one letter :-) > > Why wouldn''t the Xen backends be added by appropriate -net or -drive > options? For instance, qemu -drive file=foo.img,if=xen -net nic,model=xenThey can and I plan to support exactly that syntax ;) Lets us step back a bit. xenbus handles all the device discovery. xenstore holds (among other things) all the information xenbus needs to discover devices. The xen frontend devices (guest side of the split drivers) have the configuration information in this xenstore path: /local/domain/$domid/device/$type/$nr/ The xen backend devices (host side of the split drivers) have the configuration information in this path: /local/domain/$domid/backend/$type/$domid/$nr/ where the first domid is where the backend runs in (which is Domain-0 unless you are using driver domains). The second domid is the guest domain. The $type doesn''t need to be identical, so you can have different backend drivers serving the same frontend driver. Which actually is the case for block devices (blkback & blktap). The frontend and backend drivers usually scan their xenstore directories on initialization and install xenstore watches to see devices come and go. What needs to be done to create a xenbus device is create those two directories (one frontend, one backend) and populate the nodes therein with the information required (some generic, some device specific). All the backend drivers in my patch series are using xenstore/xenbus for device discovery as usual. The last patch (#7) actually winds up command line support: you can specify xen nics and disks then with the syntax listed above, and qemu will go and create the xenstore directories for backend and frontend. It could also be someone else creating them (xend, xenner), that will work equally well. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-29 19:11 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Daniel P. Berrange wrote:> On Tue, Jul 29, 2008 at 08:32:11AM -0500, Anthony Liguori wrote: > >> Why wouldn''t the Xen backends be added by appropriate -net or -drive >> options? For instance, qemu -drive file=foo.img,if=xen -net nic,model=xen >> > > That would work for certain setup tasks, but not all. In Xen''s branch > of QEMU, the machine init function has to set various hypervisor > parameters which aren''t directly associated with QEMU command line > args, and initialize things like the map-cache for > 4GB guest support. >map-cache is one of those things I don''t expect to ever get merged.> In the Xennite work I had further stuff going on there too - more general > initialization of Xenstore parameters - eg stuffing the name & uuid from > QEMU''s argv into right place in xenstore. >Ideally, I''d like to see Xen/KVM integration look like this: 1) Xen support is detected in configure (libxc et al) and conditionally enabled. 2) When running on bare metal, detect whether KVM acceleration is available, also detect if kqemu acceleration is available 3) When running under Xen, detect that Xen is available, and create a full virt domain 4) If a user specifies a type=xen device, it should Just Work provided you are in a Xen environment (erroring appropriately) 5) A user can explicitly specify -M xenpv. If running under Xen, this would create a Xen PV guest. If running on bare metal, Xenner would be used to present a Xen shim layer. This should work with KVM acceleration or without KVM acceleration. Bonus points if it works with kqemu too. For 1-4, the user experience should not be different at all. This of course doesn''t mean that users would be forced to interact directly with QEMU or that QEMU cannot be running within a stub domain. It would be good if these things could be automatically detected though. Regards, Anthony Liguori> Daniel >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-29 21:36 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Anthony Liguori wrote:> Daniel P. Berrange wrote: >> That would work for certain setup tasks, but not all. In Xen''s branch >> of QEMU, the machine init function has to set various hypervisor >> parameters which aren''t directly associated with QEMU command line >> args, and initialize things like the map-cache for > 4GB guest support. > > map-cache is one of those things I don''t expect to ever get merged.And the need for that will go away over time IMHO. If your Dom0 is 64bit you have no address space pressure and thus no need for mapcache. Given we have 32-on-64 and non-PAE Xen is depricated anyway there is almost no reason to not run 64bit Xen and Dom0.> Ideally, I''d like to see Xen/KVM integration look like this: > > 1) Xen support is detected in configure (libxc et al) and conditionally > enabled. > 2) When running on bare metal, detect whether KVM acceleration is > available, also detect if kqemu acceleration is available > 3) When running under Xen, detect that Xen is available, and create a > full virt domain > 4) If a user specifies a type=xen device, it should Just Work provided > you are in a Xen environment (erroring appropriately) > 5) A user can explicitly specify -M xenpv. If running under Xen, this > would create a Xen PV guest. If running on bare metal, Xenner would be > used to present a Xen shim layer. This should work with KVM > acceleration or without KVM acceleration. Bonus points if it works with > kqemu too.I''m surprised how well you can read my mind. Yes, I wanna have the bonus points ;) There are two additional points you didn''t see though: For (3) and (5) qemu should support two modes: First, attach to an existing domain. This is how Xen works today. And we want get rid of the qemu-dm fork, right? Second, optionally also create the domain, like Xenite. (4) should also just work when you are *not* in a Xen environment[1] cheers, Gerd [1] It actually does, btw. Code isn''t ready yet for merging. Stay tuned ;) -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Jul-29 21:48 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Gerd Hoffmann wrote:> Anthony Liguori wrote: > >> map-cache is one of those things I don''t expect to ever get merged. >> > > And the need for that will go away over time IMHO. If your Dom0 is > 64bit you have no address space pressure and thus no need for mapcache. > Given we have 32-on-64 and non-PAE Xen is depricated anyway there is > almost no reason to not run 64bit Xen and Dom0. >Right.>> Ideally, I''d like to see Xen/KVM integration look like this: >> >> 1) Xen support is detected in configure (libxc et al) and conditionally >> enabled. >> 2) When running on bare metal, detect whether KVM acceleration is >> available, also detect if kqemu acceleration is available >> 3) When running under Xen, detect that Xen is available, and create a >> full virt domain >> 4) If a user specifies a type=xen device, it should Just Work provided >> you are in a Xen environment (erroring appropriately) >> 5) A user can explicitly specify -M xenpv. If running under Xen, this >> would create a Xen PV guest. If running on bare metal, Xenner would be >> used to present a Xen shim layer. This should work with KVM >> acceleration or without KVM acceleration. Bonus points if it works with >> kqemu too. >> > > I''m surprised how well you can read my mind. >Scary, huh? :-)> Yes, I wanna have the bonus points ;) > > There are two additional points you didn''t see though: > > For (3) and (5) qemu should support two modes: First, attach to an > existing domain. This is how Xen works today. And we want get rid of > the qemu-dm fork, right? Second, optionally also create the domain, > like Xenite. >I have mixed feelings about this, but I don''t think there''s a way to support stub domains without this functionality. Obviously, when you run QEMU within a stub domain, the guest domain has already been created. Well, maybe it doesn''t have to be that way but it seems most reasonable to do it that way.> (4) should also just work when you are *not* in a Xen environment[1] >I considered suggesting that but figured it would be too much. I should have figured it was already working in some form :-) So how does the upstream Xen community feel about all of this? Is this a reasonable approach to merging Xen functionality into QEMU? Regards, Anthony Liguori> cheers, > Gerd > > [1] It actually does, btw. Code isn''t ready yet for merging. > Stay tuned ;) > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-30 09:20 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Anthony Liguori wrote:> Gerd Hoffmann wrote: >> +++ b/hw/xen-framebuffer.c >> @@ -0,0 +1,925 @@ >> +/* >> + * xen paravirt framebuffer backend > > No copyright? >Well, qemu-dm''s xenfb.c which I used as base didn''t have one. Guess most of this is Markus'' code (CC''ed). cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Jul-30 09:59 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Hi,>>> +/* A convenient function for munging pixels between different depths */ >>> +#define >>> BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ >>> + for (line = y ; line < (y+h) ; line++) { \>> There are similar conversion macros in the VGA code. I don''t know how >> practical it would be to reuse them but it''s at least worth looking at. > > We created a file hw/pixel_ops.h to share code between vga.c and tcx.c, > probably it can be extended to serve Xen as well.Uhm, well, it isn''t that easy. Unifying that looks like a big job in itself. pixel_ops.h as-is isn''t very helpful for xen-framebuffer. The xen-framebuffer macro isn''t very useful to others. What IMHO would be most useful is a set of generic conversion functions, operating on scanline level, much like the ones created by vga_template.h, but without dependencies on vga.c internals. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2008-Jul-30 16:31 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Gerd Hoffmann <kraxel@redhat.com> writes:> Anthony Liguori wrote: >> Gerd Hoffmann wrote: >>> +++ b/hw/xen-framebuffer.c >>> @@ -0,0 +1,925 @@ >>> +/* >>> + * xen paravirt framebuffer backend >> >> No copyright? >> > > Well, qemu-dm''s xenfb.c which I used as base didn''t have one. > Guess most of this is Markus'' code (CC''ed). > > cheers, > GerdDevelopment history has been a bit more complicated than that :) * Initial version: Anthony Liguori <anthony@codemonkey.ws> The link[*] to it has gone stale. I don''t think it had a copyright in the file that eventually mutated into the one we''re discussing. * Hacked into shape: me (xen-unstable changeset 12678). No copyright in the source file. Changeset carries: Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> * Additional changes (use "hg log tools/xenfb in xen-unstable" to list them) by: Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@suse.de> Signed-off-by: Hirofumi Tsujimura <tsujimura.hirof@jp.fujitsu.com> Signed-off-by: Ian Campbell <ian.campbell@xensource.com> Signed-off-by: John Levon <john.levon@sun.com> Signed-off-by: Junko Ichino <ichino.junko@jp.fujitsu.com> Signed-off-by: Keir Fraser <keir@xensource.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Steven Smith <sos22@cam.ac.uk> Signed-off-by: Takanori Kasai <kasai.takanori@jp.fujitsu.com> Signed-off-by: Tsunehisa Doi <Doi.Tsunehisa@jp.fujitsu.com> * Transformed from a separate program into a part of ioemu-dm: Signed-off-by: Daniel P. Berrange <berrange@redhat.com> * Additional changes (use "hg log tools/xenfb" to list them) by: Signed-off-by: Daniel P. Berrange <berrange@redhat.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com> Signed-off-by: Keir Fraser <keir@xensource.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Pat Campbell <plc@novell.com> Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com> Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> * Transformed to work on top of his common backend driver code in upstream QEMU: Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> I guess a copyright note should include at least Anthony, me, Dan, Pat, Gerd (in order of first major contribution). Perhaps something like: * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com> * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>, * Daniel P. Berrange <berrange@redhat.com>, * Gerd Hoffmann <kraxel@redhat.com> * Copyright (C) 2008 Pat Campbell <plc@novell.com> Apologies if I missed someone! [*] http://www.cs.utexas.edu/users/aliguori/xen-vfb-20060124.bundle _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-01 14:57 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Gerd Hoffmann wrote:> Hi, > > >>>> +/* A convenient function for munging pixels between different depths */ >>>> +#define >>>> BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ >>>> + for (line = y ; line < (y+h) ; line++) { \ >>>> > > >>> There are similar conversion macros in the VGA code. I don''t know how >>> practical it would be to reuse them but it''s at least worth looking at. >>> >> We created a file hw/pixel_ops.h to share code between vga.c and tcx.c, >> probably it can be extended to serve Xen as well. >> > > Uhm, well, it isn''t that easy. Unifying that looks like a big job in > itself. pixel_ops.h as-is isn''t very helpful for xen-framebuffer. The > xen-framebuffer macro isn''t very useful to others. >Okay, the xen-framebuffer macros aren''t that big so it''s not a huge deal. Was worth looking into though. Thanks! Regards, Anthony Liguori> What IMHO would be most useful is a set of generic conversion functions, > operating on scanline level, much like the ones created by > vga_template.h, but without dependencies on vga.c internals. > > cheers, > Gerd > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-01 15:05 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Markus Armbruster wrote:> Gerd Hoffmann <kraxel@redhat.com> writes: > > > I guess a copyright note should include at least Anthony, me, Dan, > Pat, Gerd (in order of first major contribution). Perhaps something > like: > > * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com> >Please add my copyright as: * Copyright IBM, Corp. 2005-2006 * * Authors: * Anthony Liguori <aliguori@us.ibm.com> Regards, Anthony Liguori> * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>, > * Daniel P. Berrange <berrange@redhat.com>, > * Gerd Hoffmann <kraxel@redhat.com> > * Copyright (C) 2008 Pat Campbell <plc@novell.com> > > Apologies if I missed someone! > > [*] http://www.cs.utexas.edu/users/aliguori/xen-vfb-20060124.bundle >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi folks, This is the second version of the xen support for qemu patch series, addressing review comments. Lots of little changes, the largest change probably is the switch-over to the esisting sys-queue.h list implementation instead of bringing in a copy of the linux kernel list.h header. The console and framebuffer backend drivers are largely based on the xen code, the other bits are rewritten from scratch. Nevertheless the code should be functionally identical. Overview (individual patches have longer descriptions): #1 -- groundwork: build system, cmd line options, ... #2 -- xen backend driver infrastructrure #3 -- xen console backend driver #4 -- xen framebuffer backend driver #5 -- xen block backend driver #6 -- xen nic backend driver #7 -- allow xen disks and nics being configured via qemu command line options. With the first four patches in place upstream qemu can replace xen''s qemu-dm for paravirtual domains. The block and nic backend drivers are full userspace implementations using the grant table device (gntdev). xen support is implemented using another machine type. xen''s qemu-dm already uses the machine type to switch between paravirtualized and fully virtualized machines, so this was the natural choice. qemu has gets a new "xenpv" machine type additionally to the "pc" and "isapc" ones. Comments? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:41 UTC
[Xen-devel] [PATCH 1/7] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -domid command line option. - allow xenpv machines run without disk and kernel specified by adding a nodisk_ok field to QEMUMachine. - detect xen being present. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 8 +++ configure | 27 +++++++++ hw/boards.h | 4 + hw/xen-machine.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen.h | 18 ++++++ target-i386/machine.c | 3 + vl.c | 19 ++++++- 7 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 hw/xen-machine.c create mode 100644 hw/xen.h diff --git a/Makefile.target b/Makefile.target index 42162c3..7edef6c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) LIBS += $(CONFIG_VNC_TLS_LIBS) endif +# xen backend driver support +XEN_OBJS := xen-machine.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 0a3b7c9..5111b73 100755 --- a/configure +++ b/configure @@ -108,6 +108,7 @@ uname_release="" curses="yes" nptl="yes" mixemu="no" +xen="no" # OS specific targetos=`uname -s` @@ -202,6 +203,7 @@ linux="yes" linux_user="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" + xen="yes" audio_possible_drivers="$audio_possible_drivers fmod" fi ;; @@ -285,6 +287,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --enable-profiler) profiler="yes" @@ -421,6 +425,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of additional emulated audio cards" echo " Available cards: ac97 adlib cs4231a gus" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" @@ -681,6 +686,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xs.h> +#include <xenctrl.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -917,6 +938,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1168,6 +1190,11 @@ if test "$brlapi" = "yes" ; then echo "#define CONFIG_BRLAPI 1" >> $config_h echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak fi +if test "$xen" = "yes" ; then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then diff --git a/hw/boards.h b/hw/boards.h index 22ac332..9af9939 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -16,6 +16,7 @@ typedef struct QEMUMachine { QEMUMachineInitFunc *init; #define RAMSIZE_FIXED (1 << 0) ram_addr_t ram_require; + int nodisk_ok; struct QEMUMachine *next; } QEMUMachine; @@ -29,6 +30,9 @@ extern QEMUMachine bareetraxfs_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen-machine.c b/hw/xen-machine.c new file mode 100644 index 0000000..7757bde --- /dev/null +++ b/hw/xen-machine.c @@ -0,0 +1,146 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007,08 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "boards.h" + +#include <xenctrl.h> + +/* -------------------------------------------------------------------- */ +/* variables */ + +int xen_domid; +int xen_present = -1; +int xen_emulate; +int xen_domainbuild; + +/* -------------------------------------------------------------------- */ +/* initialization code */ + +/* + * Figure the environment we are running in. + * Returns true when xen is present, false otherwise. + * Also checks whenever the domain specified via -domid + * exists (so we can attach) or whenever it must be created. + */ +int xen_detect(void) +{ + struct xc_dominfo info; + int xc, rc; + + if (-1 != xen_present) + goto out; + + /* running on xen? (priviledged domain) */ + xen_present = 0; + xc = xc_interface_open(); + if (-1 == xc) + goto out; + xen_present = 1; + + /* does the domain exist? Or should we create one? */ + rc = xc_domain_getinfo(xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) + xen_domainbuild = 1; + close(xc); + +out: + return xen_present; +} + +static int xen_init(void) +{ + if (!xen_domid) { + fprintf(stderr, "%s: no domid specified\n", __FUNCTION__); + return -1; + } + + if (!xen_detect()) { + fprintf(stderr, "%s: emulating Xen\n", __FUNCTION__); + xen_emulate = 1; + } + + return 0; +} + +static int xen_init_pv(DisplayState *ds) +{ + int rc; + + rc = xen_init(); + if (rc < 0) + return rc; + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* paravirtualized xen machine */ + +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + int rc; + + rc = xen_init_pv(ds); + if (-1 == rc) + goto err; + + if (xen_emulate) { + fprintf(stderr, "xen pv emulation not implemented yet\n"); + goto err; + } + if (xen_domainbuild) { + fprintf(stderr, "xen pv domain creation not implemented yet\n"); + goto err; + } + + /* create dummy cpu, halted */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; + + return; + +err: + exit(1); +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "paravirtualized Xen machine", + .init = xenpv_init, + .nodisk_ok = 1, +}; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..a772aad --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,18 @@ +#ifndef QEMU_XEN_H +#define QEMU_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +extern int xen_domid; +extern int xen_present; +extern int xen_emulate; +extern int xen_domainbuild; + +int xen_detect(void); + +#endif /* QEMU_XEN_H */ diff --git a/target-i386/machine.c b/target-i386/machine.c index 91dbd55..98ece17 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index e929370..c3f2185 100644 --- a/vl.c +++ b/vl.c @@ -29,6 +29,7 @@ #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" +#include "hw/xen.h" #include "net.h" #include "console.h" #include "sysemu.h" @@ -7744,6 +7745,9 @@ static void help(int exitcode) "-startdate select initial date of the clock\n" "-icount [N|auto]\n" " Enable virtual instruction counter with 2^N clock ticks per instruction\n" +#ifdef CONFIG_XEN + "-domid specify xen guest domain id\n" +#endif "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7849,6 +7853,9 @@ enum { QEMU_OPTION_startdate, QEMU_OPTION_tb_size, QEMU_OPTION_icount, +#ifdef CONFIG_XEN + QEMU_OPTION_domid, +#endif }; typedef struct QEMUOption { @@ -7937,6 +7944,9 @@ const QEMUOption qemu_options[] = { #ifdef CONFIG_CURSES { "curses", 0, QEMU_OPTION_curses }, #endif +#ifdef CONFIG_XEN + { "domid", HAS_ARG, QEMU_OPTION_domid }, +#endif /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -8784,6 +8794,11 @@ int main(int argc, char **argv) icount_time_shift = strtol(optarg, NULL, 0); } break; +#ifdef CONFIG_XEN + case QEMU_OPTION_domid: + xen_domid = atoi(optarg); + break; +#endif } } } @@ -8858,9 +8873,9 @@ int main(int argc, char **argv) linux_boot = (kernel_filename != NULL); net_boot = (boot_devices_bitmap >> (''n'' - ''a'')) & 0xF; - /* XXX: this should not be: some embedded targets just have flash */ + /* need a disk for this machine to boot ? */ if (!linux_boot && net_boot == 0 && - nb_drives_opt == 0) + !machine->nodisk_ok && nb_drives_opt == 0) help(1); if (!linux_boot && *kernel_cmdline != ''\0'') { -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.c | 665 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-backend.h | 87 +++++++ hw/xen-common.h | 38 +++ hw/xen-machine.c | 7 +- 5 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 hw/xen-backend.c create mode 100644 hw/xen-backend.h create mode 100644 hw/xen-common.h diff --git a/Makefile.target b/Makefile.target index 7edef6c..65f4910 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o +XEN_OBJS := xen-machine.o xen-backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.c b/hw/xen-backend.c new file mode 100644 index 0000000..8f5244a --- /dev/null +++ b/hw/xen-backend.c @@ -0,0 +1,665 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static const char *root = "/local/domain/0/backend"; +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct XenDevice *xen_be_get_xendev(char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + if (!xendev) + return NULL; + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d", + root, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can''t open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (NULL == node || 0 == strcmp(node, "online")) { + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (NULL == node || 0 == strcmp(node, "state")) { + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (NULL == node || 0 == strcmp(node, "protocol")) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[BUFSIZE]; + int be_state; + + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (NULL == xendev->fe) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev) +{ + if (xendev->be_state == XenbusStateClosed) + return; + if (xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + xen_be_set_state(xendev, XenbusStateClosed); +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[BUFSIZE], token[BUFSIZE]; + char **dev = NULL; + unsigned int cdev, j; + + /* setup watch */ + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (NULL == xendev) + continue; + xen_be_check_state(xendev); + } + qemu_free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[BUFSIZE]; + unsigned int len, dev; + + len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (-1 == dev) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (NULL == vec) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (-1 == xen_xc) { + fprintf(stderr, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (-1 == xendev->local_port) { + xendev->local_port = 0; + return -1; + } + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen-backend.h b/hw/xen-backend.h new file mode 100644 index 0000000..13b3190 --- /dev/null +++ b/hw/xen-backend.h @@ -0,0 +1,87 @@ +#ifndef QEMU_XEN_BACKEND_H +#define QEMU_XEN_BACKEND_H 1 + +#include "xen-common.h" + +/* ------------------------------------------------------------- */ + +#define BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_domid; /* set by cmd line option, in vl.c */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-common.h b/hw/xen-common.h new file mode 100644 index 0000000..866049a --- /dev/null +++ b/hw/xen-common.h @@ -0,0 +1,38 @@ +#ifndef QEMU_XEN_COMMON_H +#define QEMU_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +/* ------------------------------------------------------------- */ +/* useful defines */ + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#endif /* QEMU_XEN_COMMON_H */ diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 7757bde..f0ebd60 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -25,7 +25,7 @@ #include "hw.h" #include "boards.h" -#include <xenctrl.h> +#include "xen-backend.h" /* -------------------------------------------------------------------- */ /* variables */ @@ -81,6 +81,11 @@ static int xen_init(void) xen_emulate = 1; } + if (-1 == xen_be_init()) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + return -1; + } + return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:41 UTC
[Xen-devel] [PATCH 3/7] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen-backend.h | 3 + hw/xen-console.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 3 + 4 files changed, 277 insertions(+), 0 deletions(-) create mode 100644 hw/xen-console.c diff --git a/Makefile.target b/Makefile.target index 65f4910..f61458c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,6 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS += xen-console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 13b3190..61c37ac 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -84,4 +84,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-console.c b/hw/xen-console.c new file mode 100644 index 0000000..57bd3a1 --- /dev/null +++ b/hw/xen-console.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type; + + if (!serial_hds[con->xendev.dev]) { + xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev); + return -1; + } + + /* setup */ + snprintf(con->console, sizeof(con->console), + "/local/domain/%d/console", con->xendev.dom); + con->chr = serial_hds[con->xendev.dev]; + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + return 0; +} + +static int con_connect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) + return -1; + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) + return -1; + if (0 == xenstore_read_int(con->console, "limit", &limit)) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index f0ebd60..de22cdb 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -97,6 +97,9 @@ static int xen_init_pv(DisplayState *ds) if (rc < 0) return rc; + /* xenbus backend drivers */ + xen_be_register("console", &xen_console_ops); + return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:41 UTC
[Xen-devel] [PATCH 4/7] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than unstable (aka son-to-be 3.3). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 4 + hw/xen-framebuffer.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 5 + 4 files changed, 943 insertions(+), 1 deletions(-) create mode 100644 hw/xen-framebuffer.c diff --git a/Makefile.target b/Makefile.target index f61458c..39db580 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o +XEN_OBJS += xen-console.o xen-framebuffer.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 61c37ac..f7dd0c8 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -86,5 +86,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... /* actual backend drivers */ struct XenDevOps xen_console_ops; /* xen_console.c */ +struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_set_display(int domid, DisplayState *ds); #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c new file mode 100644 index 0000000..d406c22 --- /dev/null +++ b/hw/xen-framebuffer.c @@ -0,0 +1,933 @@ +/* + * xen paravirt framebuffer backend + * + * Copyright IBM, Corp. 2005-2006 + * Copyright Red Hat, Inc. 2006-2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com>, + * Markus Armbruster <armbru@redhat.com>, + * Daniel P. Berrange <berrange@redhat.com>, + * Pat Campbell <plc@novell.com>, + * Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct common { + struct XenDevice xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct XenInput { + struct common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct XenFB { + struct common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int feature_update; + int refresh_period; + int bug_trigger; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct common *c) +{ + int mfn; + + if (-1 == xenstore_read_fe_int(&c->xendev, "page-ref", &mfn)) + return -1; + if (-1 == xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port)) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (NULL == c->page) + return -1; + return 0; +} + +static void common_unbind(struct common *c) +{ + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } + xen_be_unbind_evtchn(&c->xendev); +} + +/* -------------------------------------------------------------------- */ + +/* + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; + +static unsigned char scancode2linux[512]; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct XenInput *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct XenInput *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct XenInput *xenfb, + int abs_x, int abs_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.pos.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct XenInput *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct XenInput *xenfb = opaque; + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (xenfb->c.ds->width - 1) / 0x7fff, + dy * (xenfb->c.ds->height - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + static int first = 1; + int i; + + if (first) { + /* Prepare scancode mapping table */ + for (i = 0; i < 128; i++) { + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + scancode2linux[i | 0x80] + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + } + first = 0; + } + + if (!in->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "request-abs-pointer", &in->abs_pointer_wanted)) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (0 != rc) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct XenDevice *xendev) +{ + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbmfns; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (0 == ptr32[1]) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs); + fbmfns = qemu_mallocz(sizeof(unsigned long) * n_fbmfns); + if (!pgmfns || !fbmfns) + goto out; + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + qemu_free(pgmfns); + qemu_free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride < 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(xenfb->c.ds->data \ + + (line * xenfb->c.ds->linesize) \ + + (x * xenfb->c.ds->depth / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + xenfb->c.ds->depth / 8); \ + } \ + } + + +/* This copies data from the guest framebuffer region, into QEMU''s copy + * NB. QEMU''s copy is stored in the pixel format of a) the local X + * server (SDL case) or b) the current VNC client pixel format. + * When shifting between colour depths we preserve the MSB. + */ +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) +{ + int line; + + if (1 /* !xenfb->c.ds->shared_buf */) { + if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */ + for (line = y ; line < (y+h) ; line++) { + memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8), + xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8), + w * xenfb->depth / 8); + } + } else { /* Mismatch requires slow pixel munging */ + /* 8 bit == r:3 g:3 b:2 */ + /* 16 bit == r:5 g:6 b:5 */ + /* 24 bit == r:8 g:8 b:8 */ + /* 32 bit == r:8 g:8 b:8 (padding:8) */ + if (xenfb->depth == 8) { + if (xenfb->c.ds->depth == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } + } else if (xenfb->depth == 16) { + if (xenfb->c.ds->depth == 8) { + BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8); + } + } else if (xenfb->depth == 24 || xenfb->depth == 32) { + if (xenfb->c.ds->depth == 8) { + BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2); + } else if (xenfb->c.ds->depth == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } + } + } + } + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct XenFB *xenfb = opaque; + int i; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period; + + if (xenfb_queue_full(xenfb)) + return; + + /* + * TODO: xen''s qemu-dm seems to have some patches to + * make the qemu display code avoid unneeded + * work. + * - Port them over. + * - Put ds->idle back into use then. + * - Same goes for ds->shared_buf btw. + */ + if (0 /* xenfb->c.ds->idle */) + period = XENFB_NO_REFRESH; + else { + period = xenfb->c.ds->gui_timer_interval; + if (!period) + period = 30; // GUI_REFRESH_INTERVAL (see vl.c) + } + + if (xenfb->refresh_period != period) { + dprintf("%s: %d\n", __FUNCTION__, period); + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) { + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n"); + dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +static void xenfb_handle_events(struct XenFB *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "xen be: %s: bogus update ignored\n", + xenfb->c.xendev.name); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "xen be: %s: bogus update clipped\n", + xenfb->c.xendev.name); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct XenFB *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static int fb_init(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + if (!fb->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update)) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + if (-1 == xenstore_read_fe_int(xendev, "videoram", &videoram)) + videoram = 0; + + rc = common_bind(&fb->c); + if (0 != rc) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (0 != rc) + return rc; + + rc = xenfb_map_fb(fb); + if (0 != rc) + return rc; + + graphic_console_init(fb->c.ds, + xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + + return 0; +} + +static void fb_disconnect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* FIXME: Hmm, un-init gfx display? can qemu handle that? */ + common_unbind(&fb->c); +} + +static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (0 == fb->bug_trigger && 0 == strcmp(node, "state") && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct XenDevice *xendev) +{ + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_kbdmouse_ops = { + .size = sizeof(struct XenInput), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct XenDevOps xen_framebuffer_ops = { + .size = sizeof(struct XenFB), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +static void xen_set_display_type(int domid, char *type, DisplayState *ds) +{ + struct XenDevice *xendev; + struct common *c; + + xendev = xen_be_find_xendev(type, domid, 0); + if (!xendev) + return; + c = container_of(xendev, struct common, xendev); + c->ds = ds; + xen_be_printf(xendev, 1, "ds is %p\n", ds); + /* retry ->init() */ + xen_be_check_state(xendev); +} + +void xen_set_display(int domid, DisplayState *ds) +{ + xen_set_display_type(domid, "vkbd", ds); + xen_set_display_type(domid, "vfb", ds); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index de22cdb..8d7140a 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -99,6 +99,11 @@ static int xen_init_pv(DisplayState *ds) /* xenbus backend drivers */ xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_set_display(xen_domid, ds); return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:41 UTC
[Xen-devel] [PATCH 5/7] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 2 + hw/xen-blkif.h | 103 ++++++++ hw/xen-disk.c | 677 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 1 + sysemu.h | 2 +- vl.c | 4 + 7 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 hw/xen-blkif.h create mode 100644 hw/xen-disk.c diff --git a/Makefile.target b/Makefile.target index 39db580..e668bd2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index f7dd0c8..7489cbe 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -2,6 +2,7 @@ #define QEMU_XEN_BACKEND_H 1 #include "xen-common.h" +#include "sysemu.h" /* ------------------------------------------------------------- */ @@ -88,6 +89,7 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... struct XenDevOps xen_console_ops; /* xen_console.c */ struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-blkif.h b/hw/xen-blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen-blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen-disk.c b/hw/xen-disk.c new file mode 100644 index 0000000..96fa9dc --- /dev/null +++ b/hw/xen-disk.c @@ -0,0 +1,677 @@ +/* + * xen paravirt block device backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * FIXME: the code is designed to handle multiple outstanding + * requests (using aio or using threads), which isn''t used right + * now due to limitations of the qemu block driver interface. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen-blkif.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start, end; + struct iovec vec[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int vecs; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + struct XenBlkDev *blkdev; + LIST_ENTRY(ioreq) list; +}; + +struct XenBlkDev { + struct XenDevice xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *fileproto; + char *filename; + int ring_ref; + void *sring; + int file; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + LIST_HEAD(inflight_head, ioreq) inflight; + LIST_HEAD(finished_head, ioreq) finished; + LIST_HEAD(freelist_head, ioreq) freelist; + int requests; + + /* qemu block driver */ + BlockDriverState *bs; +}; + +static int syncwrite = 0; +static int batch_maps = 0; +static int max_requests = 32; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (LIST_EMPTY(&blkdev->freelist)) { + if (blkdev->requests >= max_requests) + goto out; + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + if (NULL == ioreq) + goto out; + ioreq->blkdev = blkdev; + blkdev->requests++; + } else { + /* get one from freelist */ + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + } + LIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + LIST_INSERT_HEAD(&blkdev->finished, ioreq, list); +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + LIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); +} + +/* + * translate request into iovec + start offset + end offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->end = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + + ioreq->vec[i].iov_base = (void*)mem; + ioreq->vec[i].iov_len = len; + ioreq->end += len; + } + if (ioreq->end > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + ioreq->vecs = i; + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + if (!ioreq->pages) + return; + if (0 != xc_gnttab_munmap(gnt, ioreq->pages, ioreq->vecs)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->vecs; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->vecs; i++) { + if (!ioreq->page[i]) + continue; + if (0 != xc_gnttab_munmap(gnt, ioreq->page[i], 1)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->vecs, ioreq->domids, ioreq->refs, ioreq->prot); + if (NULL == ioreq->pages) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->vecs, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->vecs; i++) + ioreq->vec[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->vecs; + } else { + for (i = 0; i < ioreq->vecs; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (NULL == ioreq->page[i]) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->vec[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (-1 == ioreq_map(ioreq)) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq; + int send_notify = 0; + + while (!LIST_EMPTY(&blkdev->finished)) { + ioreq = LIST_FIRST(&blkdev->finished); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct XenBlkDev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + do { + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + /* Limit #of requests we queue up for I/O so we ack requests + * faster if busy. Improves backend/frontend parallelism and + * reduces evchn signaling. */ + if (rp > rc + (max_requests >> 2)) { + rp = rc + (max_requests >> 2); + blkdev->more_work++; + } + + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (NULL == ioreq) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (0 != ioreq_parse(ioreq)) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + /* run i/o in qemu mode */ + ioreq_runio_qemu(ioreq); + ioreq_finish(ioreq); + } + blk_send_response_all(blkdev); + + } while (blkdev->more_work); +} + +/* ------------------------------------------------------------- */ + +static void blk_alloc(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + LIST_INIT(&blkdev->inflight); + LIST_INIT(&blkdev->finished); + LIST_INIT(&blkdev->freelist); +} + +static int blk_init(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int mode, qflags, have_barriers, index, info = 0; + char *h; + + /* read xenstore entries */ + if (NULL == blkdev->params) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (NULL != (h = strchr(blkdev->params, '':''))) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (NULL == blkdev->mode) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (NULL == blkdev->type) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (NULL == blkdev->dev) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + + /* do we have all we need? */ + if (NULL == blkdev->params || + NULL == blkdev->mode || + NULL == blkdev->type || + NULL == blkdev->dev) + return -1; + + /* read-only ? */ + if (0 == strcmp(blkdev->mode, "w")) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; + } + + /* init qemu block driver */ + index = (blkdev->xendev.dev - 202 * 256) / 16; + index = drive_get_index(IF_XEN, 0, index); + if (-1 == index) { + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (0 != bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto))) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + blkdev->bs = drives_table[index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) + blkdev->file_size = 0; + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port)) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32)) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64)) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (-1 != blkdev->file) { + close(blkdev->file); + blkdev->file = -1; + } + if (blkdev->bs) { + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + struct ioreq *ioreq; + + while (!LIST_EMPTY(&blkdev->freelist)) { + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_free(ioreq); + } + + qemu_free(blkdev->params); + qemu_free(blkdev->mode); + return 0; +} + +static void blk_event(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + blk_handle_requests(blkdev); +} + +struct XenDevOps xen_blkdev_ops = { + .size = sizeof(struct XenBlkDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 8d7140a..a1289c1 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -101,6 +101,7 @@ static int xen_init_pv(DisplayState *ds) xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/sysemu.h b/sysemu.h index b12fae0..49e75b1 100644 --- a/sysemu.h +++ b/sysemu.h @@ -113,7 +113,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_XEN } BlockInterfaceType; typedef struct DriveInfo { diff --git a/vl.c b/vl.c index c3f2185..a80a46e 100644 --- a/vl.c +++ b/vl.c @@ -5484,6 +5484,9 @@ static int drive_init(struct drive_opt *arg, int snapshot, } else if (!strcmp(buf, "sd")) { type = IF_SD; max_devs = 0; + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; @@ -5669,6 +5672,7 @@ static int drive_init(struct drive_opt *arg, int snapshot, switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 1 + hw/xen-machine.c | 1 + hw/xen-nic.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 383 insertions(+), 1 deletions(-) create mode 100644 hw/xen-nic.c diff --git a/Makefile.target b/Makefile.target index e668bd2..85f938c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 7489cbe..b484752 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -90,6 +90,7 @@ struct XenDevOps xen_console_ops; /* xen_console.c */ struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-machine.c b/hw/xen-machine.c index a1289c1..c8fb256 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -102,6 +102,7 @@ static int xen_init_pv(DisplayState *ds) xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/hw/xen-nic.c b/hw/xen-nic.c new file mode 100644 index 0000000..b533a60 --- /dev/null +++ b/hw/xen-nic.c @@ -0,0 +1,380 @@ +/* + * xen paravirt network card backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct XenNetDev { + struct XenDevice xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct XenNetDev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) + net_checksum_calculate(page + txreq.offset, txreq.size); + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct XenNetDev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct XenNetDev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct XenNetDev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (NULL == netdev->mac) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (NULL == netdev->mac) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, net_rx_packet, net_rx_ok, netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + int rx_copy; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref)) + return 1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port)) + return -1; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy)) + rx_copy = 0; + if (0 == rx_copy) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } +} + +static void net_event(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + net_tx_packets(netdev); +} + +/* ------------------------------------------------------------- */ + +struct XenDevOps xen_netdev_ops = { + .size = sizeof(struct XenNetDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, +}; -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:41 UTC
[Xen-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 7 +++ hw/xen-config.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 19 +++++++- 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 hw/xen-config.c diff --git a/Makefile.target b/Makefile.target index 85f938c..a3f8d24 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS := xen-machine.o xen-backend.o xen-config.o XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index b484752..c1da2ca 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -3,6 +3,8 @@ #include "xen-common.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* ------------------------------------------------------------- */ @@ -94,4 +96,9 @@ struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-config.c b/hw/xen-config.c new file mode 100644 index 0000000..4156f82 --- /dev/null +++ b/hw/xen-config.c @@ -0,0 +1,139 @@ +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + TAILQ_ENTRY(xs_dirs) list; +}; +static TAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = TAILQ_HEAD_INITIALIZER(xs_cleanup); + +static void xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = qemu_malloc(sizeof(*d)); + if (!d) + return; + d->xs_dir = dir; + TAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + fprintf(stderr, "xen be: %s\n", __FUNCTION__); + TAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms = { + .id = xen_domid, + .perms = p, + }; + + if (!xs_mkdir(xenstore, 0, dev)) { + fprintf(stderr, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(qemu_strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, &perms, 1)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(char *ftype, char *btype, int vdev, + char *fe, char *be, int len) +{ + snprintf(fe, len, "/local/domain/%d/device/%s/%d", + xen_domid, ftype, vdev); + snprintf(be, len, "/local/domain/0/backend/%s/%d/%d", + btype, xen_domid, vdev); + + xen_config_dev_mkdir(fe, XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ +#if 0 + xenstore_write_str(fe, "protocol", + xen_config_dev_protocol(xen)); +#endif + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + char *devtype = cdrom ? "cdrom" : "disk"; + char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + fprintf(stderr, "xen be: config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + fprintf(stderr, "xen be: config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index c8fb256..4e4d893 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -121,7 +121,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; - int rc; + int index,i,rc; rc = xen_init_pv(ds); if (-1 == rc) @@ -147,6 +147,23 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, env = cpu_init(cpu_model); env->halted = 1; + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (nd_table[i].model && 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); return; err: -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi folks, This is the second version of the xen support for qemu patch series, addressing review comments. Lots of little changes, the largest change probably is the switch-over to the esisting sys-queue.h list implementation instead of bringing in a copy of the linux kernel list.h header. The console and framebuffer backend drivers are largely based on the xen code, the other bits are rewritten from scratch. Nevertheless the code should be functionally identical. Overview (individual patches have longer descriptions): #1 -- groundwork: build system, cmd line options, ... #2 -- xen backend driver infrastructrure #3 -- xen console backend driver #4 -- xen framebuffer backend driver #5 -- xen block backend driver #6 -- xen nic backend driver #7 -- allow xen disks and nics being configured via qemu command line options. With the first four patches in place upstream qemu can replace xen''s qemu-dm for paravirtual domains. The block and nic backend drivers are full userspace implementations using the grant table device (gntdev). xen support is implemented using another machine type. xen''s qemu-dm already uses the machine type to switch between paravirtualized and fully virtualized machines, so this was the natural choice. qemu has gets a new "xenpv" machine type additionally to the "pc" and "isapc" ones. Comments? cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:50 UTC
[Xen-devel] [PATCH 1/7] xen: groundwork for xen support
- configure script and build system changes. - wind up new machine type. - add -domid command line option. - allow xenpv machines run without disk and kernel specified by adding a nodisk_ok field to QEMUMachine. - detect xen being present. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 8 +++ configure | 27 +++++++++ hw/boards.h | 4 + hw/xen-machine.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen.h | 18 ++++++ target-i386/machine.c | 3 + vl.c | 19 ++++++- 7 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 hw/xen-machine.c create mode 100644 hw/xen.h diff --git a/Makefile.target b/Makefile.target index 42162c3..7edef6c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -515,6 +515,14 @@ CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS) LIBS += $(CONFIG_VNC_TLS_LIBS) endif +# xen backend driver support +XEN_OBJS := xen-machine.o +ifeq ($(CONFIG_XEN), yes) + OBJS += $(XEN_OBJS) + LIBS += $(XEN_LIBS) + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes +endif + # SCSI layer OBJS+= lsi53c895a.o esp.o diff --git a/configure b/configure index 0a3b7c9..5111b73 100755 --- a/configure +++ b/configure @@ -108,6 +108,7 @@ uname_release="" curses="yes" nptl="yes" mixemu="no" +xen="no" # OS specific targetos=`uname -s` @@ -202,6 +203,7 @@ linux="yes" linux_user="yes" if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then kqemu="yes" + xen="yes" audio_possible_drivers="$audio_possible_drivers fmod" fi ;; @@ -285,6 +287,8 @@ for opt do ;; --disable-kqemu) kqemu="no" ;; + --disable-xen) xen="no" + ;; --disable-brlapi) brlapi="no" ;; --enable-profiler) profiler="yes" @@ -421,6 +425,7 @@ echo " Available drivers: $audio_possible_drivers" echo " --audio-card-list=LIST set list of additional emulated audio cards" echo " Available cards: ac97 adlib cs4231a gus" echo " --enable-mixemu enable mixer emulation" +echo " --disable-xen disable xen backend driver support" echo " --disable-brlapi disable BrlAPI" echo " --disable-vnc-tls disable TLS encryption for VNC server" echo " --disable-curses disable curses output" @@ -681,6 +686,22 @@ else fi ########################################## +# xen probe + +if test "$xen" = "yes" ; then +cat > $TMPC <<EOF +#include <xs.h> +#include <xenctrl.h> +int main(void) { xs_daemon_open; xc_interface_open; } +EOF + if $cc $ARCH_CFLAGS -c -o $TMPO $TMPC -lxenstore -lxenctrl 2> /dev/null ; then + : + else + xen="no" + fi +fi + +########################################## # SDL probe sdl_too_old=no @@ -917,6 +938,7 @@ if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi echo "kqemu support $kqemu" +echo "xen support $xen" echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ @@ -1168,6 +1190,11 @@ if test "$brlapi" = "yes" ; then echo "#define CONFIG_BRLAPI 1" >> $config_h echo "BRLAPI_LIBS=-lbrlapi" >> $config_mak fi +if test "$xen" = "yes" ; then + echo "CONFIG_XEN=yes" >> $config_mak + echo "#define CONFIG_XEN 1" >> $config_h + echo "XEN_LIBS=-lxenstore -lxenctrl" >> $config_mak +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then diff --git a/hw/boards.h b/hw/boards.h index 22ac332..9af9939 100644 --- a/hw/boards.h +++ b/hw/boards.h @@ -16,6 +16,7 @@ typedef struct QEMUMachine { QEMUMachineInitFunc *init; #define RAMSIZE_FIXED (1 << 0) ram_addr_t ram_require; + int nodisk_ok; struct QEMUMachine *next; } QEMUMachine; @@ -29,6 +30,9 @@ extern QEMUMachine bareetraxfs_machine; extern QEMUMachine pc_machine; extern QEMUMachine isapc_machine; +/* xen_machine.c */ +extern QEMUMachine xenpv_machine; + /* ppc.c */ extern QEMUMachine prep_machine; extern QEMUMachine core99_machine; diff --git a/hw/xen-machine.c b/hw/xen-machine.c new file mode 100644 index 0000000..7757bde --- /dev/null +++ b/hw/xen-machine.c @@ -0,0 +1,146 @@ +/* + * QEMU Xen PV Machine + * + * Copyright (c) 2007,08 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw.h" +#include "boards.h" + +#include <xenctrl.h> + +/* -------------------------------------------------------------------- */ +/* variables */ + +int xen_domid; +int xen_present = -1; +int xen_emulate; +int xen_domainbuild; + +/* -------------------------------------------------------------------- */ +/* initialization code */ + +/* + * Figure the environment we are running in. + * Returns true when xen is present, false otherwise. + * Also checks whenever the domain specified via -domid + * exists (so we can attach) or whenever it must be created. + */ +int xen_detect(void) +{ + struct xc_dominfo info; + int xc, rc; + + if (-1 != xen_present) + goto out; + + /* running on xen? (priviledged domain) */ + xen_present = 0; + xc = xc_interface_open(); + if (-1 == xc) + goto out; + xen_present = 1; + + /* does the domain exist? Or should we create one? */ + rc = xc_domain_getinfo(xc, xen_domid, 1, &info); + if ((1 != rc) || (info.domid != xen_domid)) + xen_domainbuild = 1; + close(xc); + +out: + return xen_present; +} + +static int xen_init(void) +{ + if (!xen_domid) { + fprintf(stderr, "%s: no domid specified\n", __FUNCTION__); + return -1; + } + + if (!xen_detect()) { + fprintf(stderr, "%s: emulating Xen\n", __FUNCTION__); + xen_emulate = 1; + } + + return 0; +} + +static int xen_init_pv(DisplayState *ds) +{ + int rc; + + rc = xen_init(); + if (rc < 0) + return rc; + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* paravirtualized xen machine */ + +static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, + const char *boot_device, DisplayState *ds, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + int rc; + + rc = xen_init_pv(ds); + if (-1 == rc) + goto err; + + if (xen_emulate) { + fprintf(stderr, "xen pv emulation not implemented yet\n"); + goto err; + } + if (xen_domainbuild) { + fprintf(stderr, "xen pv domain creation not implemented yet\n"); + goto err; + } + + /* create dummy cpu, halted */ + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); + env->halted = 1; + + return; + +err: + exit(1); +} + +QEMUMachine xenpv_machine = { + .name = "xenpv", + .desc = "paravirtualized Xen machine", + .init = xenpv_init, + .nodisk_ok = 1, +}; diff --git a/hw/xen.h b/hw/xen.h new file mode 100644 index 0000000..a772aad --- /dev/null +++ b/hw/xen.h @@ -0,0 +1,18 @@ +#ifndef QEMU_XEN_H +#define QEMU_XEN_H 1 +/* + * public xen header + * stuff needed outside xen-*.c, i.e. interfaces to qemu. + * must not depend on any xen headers being present in + * /usr/include/xen, so it can be included unconditionally. + */ + +/* xen-machine.c */ +extern int xen_domid; +extern int xen_present; +extern int xen_emulate; +extern int xen_domainbuild; + +int xen_detect(void); + +#endif /* QEMU_XEN_H */ diff --git a/target-i386/machine.c b/target-i386/machine.c index 91dbd55..98ece17 100644 --- a/target-i386/machine.c +++ b/target-i386/machine.c @@ -9,6 +9,9 @@ void register_machines(void) { qemu_register_machine(&pc_machine); qemu_register_machine(&isapc_machine); +#ifdef CONFIG_XEN + qemu_register_machine(&xenpv_machine); +#endif } static void cpu_put_seg(QEMUFile *f, SegmentCache *dt) diff --git a/vl.c b/vl.c index e929370..c3f2185 100644 --- a/vl.c +++ b/vl.c @@ -29,6 +29,7 @@ #include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" +#include "hw/xen.h" #include "net.h" #include "console.h" #include "sysemu.h" @@ -7744,6 +7745,9 @@ static void help(int exitcode) "-startdate select initial date of the clock\n" "-icount [N|auto]\n" " Enable virtual instruction counter with 2^N clock ticks per instruction\n" +#ifdef CONFIG_XEN + "-domid specify xen guest domain id\n" +#endif "\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" @@ -7849,6 +7853,9 @@ enum { QEMU_OPTION_startdate, QEMU_OPTION_tb_size, QEMU_OPTION_icount, +#ifdef CONFIG_XEN + QEMU_OPTION_domid, +#endif }; typedef struct QEMUOption { @@ -7937,6 +7944,9 @@ const QEMUOption qemu_options[] = { #ifdef CONFIG_CURSES { "curses", 0, QEMU_OPTION_curses }, #endif +#ifdef CONFIG_XEN + { "domid", HAS_ARG, QEMU_OPTION_domid }, +#endif /* temporary options */ { "usb", 0, QEMU_OPTION_usb }, @@ -8784,6 +8794,11 @@ int main(int argc, char **argv) icount_time_shift = strtol(optarg, NULL, 0); } break; +#ifdef CONFIG_XEN + case QEMU_OPTION_domid: + xen_domid = atoi(optarg); + break; +#endif } } } @@ -8858,9 +8873,9 @@ int main(int argc, char **argv) linux_boot = (kernel_filename != NULL); net_boot = (boot_devices_bitmap >> (''n'' - ''a'')) & 0xF; - /* XXX: this should not be: some embedded targets just have flash */ + /* need a disk for this machine to boot ? */ if (!linux_boot && net_boot == 0 && - nb_drives_opt == 0) + !machine->nodisk_ok && nb_drives_opt == 0) help(1); if (!linux_boot && *kernel_cmdline != ''\0'') { -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds infrastructure for xen backend drivers living in qemu, so drivers don''t need to implement common stuff on their own. It''s mostly xenbus management stuff: some functions to access xentore, setting up xenstore watches, callbacks on device discovery and state changes, handle event channel, ... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.c | 665 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-backend.h | 87 +++++++ hw/xen-common.h | 38 +++ hw/xen-machine.c | 7 +- 5 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 hw/xen-backend.c create mode 100644 hw/xen-backend.h create mode 100644 hw/xen-common.h diff --git a/Makefile.target b/Makefile.target index 7edef6c..65f4910 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o +XEN_OBJS := xen-machine.o xen-backend.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.c b/hw/xen-backend.c new file mode 100644 index 0000000..8f5244a --- /dev/null +++ b/hw/xen-backend.c @@ -0,0 +1,665 @@ +/* + * xen backend driver infrastructure + * (c) 2008 Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * TODO: add some xenbus / xenstore concepts overview here. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <inttypes.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/signal.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/grant_table.h> + +#include "hw.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +/* public */ +int xen_xc; +struct xs_handle *xenstore = NULL; + +/* private */ +static TAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = TAILQ_HEAD_INITIALIZER(xendevs); +static const char *root = "/local/domain/0/backend"; +static int debug = 0; + +/* ------------------------------------------------------------- */ + +int xenstore_write_str(const char *base, const char *node, const char *val) +{ + char abspath[BUFSIZE]; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + if (!xs_write(xenstore, 0, abspath, val, strlen(val))) + return -1; + return 0; +} + +char *xenstore_read_str(const char *base, const char *node) +{ + char abspath[BUFSIZE]; + unsigned int len; + + snprintf(abspath, sizeof(abspath), "%s/%s", base, node); + return xs_read(xenstore, 0, abspath, &len); +} + +int xenstore_write_int(const char *base, const char *node, int ival) +{ + char val[32]; + + snprintf(val, sizeof(val), "%d", ival); + return xenstore_write_str(base, node, val); +} + +int xenstore_read_int(const char *base, const char *node, int *ival) +{ + char *val; + int rc = -1; + + val = xenstore_read_str(base, node); + if (val && 1 == sscanf(val, "%d", ival)) + rc = 0; + qemu_free(val); + return rc; +} + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +{ + return xenstore_write_str(xendev->be, node, val); +} + +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +{ + return xenstore_write_int(xendev->be, node, ival); +} + +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->be, node); +} + +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->be, node, ival); +} + +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +{ + return xenstore_read_str(xendev->fe, node); +} + +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +{ + return xenstore_read_int(xendev->fe, node, ival); +} + +/* ------------------------------------------------------------- */ + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} + +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +{ + int rc; + + rc = xenstore_write_be_int(xendev, "state", state); + if (rc < 0) + return rc; + xen_be_printf(xendev, 1, "backend state: %s -> %s\n", + xenbus_strstate(xendev->be_state), xenbus_strstate(state)); + xendev->be_state = state; + return 0; +} + +/* ------------------------------------------------------------- */ + +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev) +{ + struct XenDevice *xendev; + + TAILQ_FOREACH(xendev, &xendevs, next) { + if (xendev->dom != dom) + continue; + if (xendev->dev != dev) + continue; + if (0 != strcmp(xendev->type, type)) + continue; + return xendev; + } + return NULL; +} + +/* + * get xen backend device, allocate a new one if it doesn''t exist. + */ +static struct XenDevice *xen_be_get_xendev(char *type, int dom, int dev, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + + xendev = xen_be_find_xendev(type, dom, dev); + if (xendev) + return xendev; + + /* init new xendev */ + xendev = qemu_mallocz(ops->size); + if (!xendev) + return NULL; + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + snprintf(xendev->be, sizeof(xendev->be), "%s/%s/%d/%d", + root, xendev->type, xendev->dom, xendev->dev); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xc_evtchn_open(); + if (xendev->evtchndev < 0) { + fprintf(stderr, "can''t open evtchn device\n"); + qemu_free(xendev); + return NULL; + } + fcntl(xc_evtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xc_gnttab_open(); + if (xendev->gnttabdev < 0) { + fprintf(stderr, "can''t open gnttab device\n"); + xc_evtchn_close(xendev->evtchndev); + qemu_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = -1; + } + + TAILQ_INSERT_TAIL(&xendevs, xendev, next); + + if (xendev->ops->alloc) + xendev->ops->alloc(xendev); + + return xendev; +} + +/* + * release xen backend device. + */ +static struct XenDevice *xen_be_del_xendev(int dom, int dev) +{ + struct XenDevice *xendev, *xnext; + + /* + * This is pretty much like TAILQ_FOREACH(xendev, &xendevs, next) but + * we save the next pointer in xnext because we might free xendev. + */ + xnext = xendevs.tqh_first; + while (xnext) { + xendev = xnext; + xnext = xendev->next.tqe_next; + + if (xendev->dom != dom) + continue; + if (xendev->dev != dev && dev != -1) + continue; + + if (xendev->ops->free) + xendev->ops->free(xendev); + + if (xendev->fe) { + char token[BUFSIZE]; + snprintf(token, sizeof(token), "fe:%p", xendev); + xs_unwatch(xenstore, xendev->fe, token); + qemu_free(xendev->fe); + } + + if (xendev->evtchndev >= 0) + xc_evtchn_close(xendev->evtchndev); + if (xendev->gnttabdev >= 0) + xc_gnttab_close(xendev->gnttabdev); + + TAILQ_REMOVE(&xendevs, xendev, next); + qemu_free(xendev); + } + return NULL; +} + +/* + * Sync internal data structures on xenstore updates. + * Node specifies the changed field. node = NULL means + * update all fields (used for initialization). + */ +static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +{ + if (NULL == node || 0 == strcmp(node, "online")) { + if (-1 == xenstore_read_be_int(xendev, "online", &xendev->online)) + xendev->online = 0; + } + + if (node) { + xen_be_printf(xendev, 2, "backend update: %s\n", node); + if (xendev->ops->backend_changed) + xendev->ops->backend_changed(xendev, node); + } +} + +static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +{ + int fe_state; + + if (NULL == node || 0 == strcmp(node, "state")) { + if (-1 == xenstore_read_fe_int(xendev, "state", &fe_state)) + fe_state = XenbusStateUnknown; + if (xendev->fe_state != fe_state) + xen_be_printf(xendev, 1, "frontend state: %s -> %s\n", + xenbus_strstate(xendev->fe_state), + xenbus_strstate(fe_state)); + xendev->fe_state = fe_state; + } + if (NULL == node || 0 == strcmp(node, "protocol")) { + qemu_free(xendev->protocol); + xendev->protocol = xenstore_read_fe_str(xendev, "protocol"); + if (xendev->protocol) + xen_be_printf(xendev, 1, "frontend protocol: %s\n", xendev->protocol); + } + + if (node) { + xen_be_printf(xendev, 2, "frontend update: %s\n", node); + if (xendev->ops->frontend_changed) + xendev->ops->frontend_changed(xendev, node); + } +} + +/* ------------------------------------------------------------- */ +/* Check for possible state transitions and perform them. */ + +/* + * Initial xendev setup. Read frontend path, register watch for it. + * Should succeed once xend finished setting up the backend device. + * + * Also sets initial state (-> Initializing) when done. Which + * only affects the xendev->be_state variable as xenbus should + * already be put into that state by xend. + */ +static int xen_be_try_setup(struct XenDevice *xendev) +{ + char token[BUFSIZE]; + int be_state; + + if (-1 == xenstore_read_be_int(xendev, "state", &be_state)) { + xen_be_printf(xendev, 0, "reading backend state failed\n"); + return -1; + } + + if (be_state != XenbusStateInitialising) { + xen_be_printf(xendev, 0, "initial backend state is wrong (%s)\n", + xenbus_strstate(be_state)); + return -1; + } + + xendev->fe = xenstore_read_be_str(xendev, "frontend"); + if (NULL == xendev->fe) { + xen_be_printf(xendev, 0, "reading frontend path failed\n"); + return -1; + } + + /* setup frontend watch */ + snprintf(token, sizeof(token), "fe:%p", xendev); + if (!xs_watch(xenstore, xendev->fe, token)) { + xen_be_printf(xendev, 0, "watching frontend path (%s) failed\n", + xendev->fe); + return -1; + } + xen_be_set_state(xendev, XenbusStateInitialising); + + xen_be_backend_changed(xendev, NULL); + xen_be_frontend_changed(xendev, NULL); + return 0; +} + +/* + * Try initialize xendev. Prepare everything the backend can do + * without synchronizing with the frontend. Fakes hotplug-status. No + * hotplug involved here because this is about userspace drivers, thus + * there are kernel backend devices which could invoke hotplug. + * + * Goes to InitWait on success. + */ +static int xen_be_try_init(struct XenDevice *xendev) +{ + int rc = 0; + + if (!xendev->online) { + xen_be_printf(xendev, 1, "not online\n"); + return -1; + } + + if (xendev->ops->init) + rc = xendev->ops->init(xendev); + if (0 != rc) { + xen_be_printf(xendev, 1, "init() failed\n"); + return rc; + } + + xenstore_write_be_str(xendev, "hotplug-status", "connected"); + xen_be_set_state(xendev, XenbusStateInitWait); + return 0; +} + +/* + * Try to connect xendev. Depends on the frontend being ready + * for it (shared ring and evtchn info in xenstore, state being + * Initialised or Connected). + * + * Goes to Connected on success. + */ +static int xen_be_try_connect(struct XenDevice *xendev) +{ + int rc = 0; + + if (xendev->fe_state != XenbusStateInitialised && + xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_be_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_be_printf(xendev, 2, "frontend not ready (yet)\n"); + return -1; + } + } + + if (xendev->ops->connect) + rc = xendev->ops->connect(xendev); + if (0 != rc) { + xen_be_printf(xendev, 0, "connect() failed\n"); + return rc; + } + + xen_be_set_state(xendev, XenbusStateConnected); + return 0; +} + +/* + * Teardown connection. + * + * Goes to Closed when done. + */ +static void xen_be_disconnect(struct XenDevice *xendev) +{ + if (xendev->be_state == XenbusStateClosed) + return; + if (xendev->ops->disconnect) + xendev->ops->disconnect(xendev); + xen_be_set_state(xendev, XenbusStateClosed); +} + +/* + * state change dispatcher function + */ +void xen_be_check_state(struct XenDevice *xendev) +{ + int rc = 0; + + /* frontend may request shutdown from almost anywhere */ + if (xendev->fe_state == XenbusStateClosing || + xendev->fe_state == XenbusStateClosed) { + xen_be_disconnect(xendev); + return; + } + + /* check for possible backend state transitions */ + for (;;) { + switch (xendev->be_state) { + case XenbusStateUnknown: + rc = xen_be_try_setup(xendev); + break; + case XenbusStateInitialising: + rc = xen_be_try_init(xendev); + break; + case XenbusStateInitWait: + rc = xen_be_try_connect(xendev); + break; + default: + rc = -1; + } + if (0 != rc) + break; + } +} + +/* ------------------------------------------------------------- */ + +static int xenstore_scan(char *type, int dom, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[BUFSIZE], token[BUFSIZE]; + char **dev = NULL; + unsigned int cdev, j; + + /* setup watch */ + snprintf(token, sizeof(token), "be:%p:%d:%p", type, dom, ops); + snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (!xs_watch(xenstore, path, token)) { + fprintf(stderr, "xen be: watching backend path (%s) failed\n", path); + return -1; + } + + /* look for backends */ + dev = xs_directory(xenstore, 0, path, &cdev); + if (!dev) + return 0; + for (j = 0; j < cdev; j++) { + xendev = xen_be_get_xendev(type, dom, atoi(dev[j]), ops); + if (NULL == xendev) + continue; + xen_be_check_state(xendev); + } + qemu_free(dev); + return 0; +} + +static void xenstore_update_be(char *watch, char *type, int dom, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[BUFSIZE]; + unsigned int len, dev; + + len = snprintf(path, sizeof(path), "%s/%s/%d", root, type, dom); + if (0 != strncmp(path, watch, len)) + return; + if (2 != sscanf(watch+len, "/%u/%255s", &dev, path)) { + strcpy(path, ""); + if (1 != sscanf(watch+len, "/%u", &dev)) + dev = -1; + } + if (-1 == dev) + return; + + if (0) { + /* FIXME: detect devices being deleted from xenstore ... */ + xen_be_del_xendev(dom, dev); + } + + xendev = xen_be_get_xendev(type, dom, dev, ops); + if (NULL != xendev) { + xen_be_backend_changed(xendev, path); + xen_be_check_state(xendev); + } +} + +static void xenstore_update_fe(char *watch, struct XenDevice *xendev) +{ + char *node; + unsigned int len; + + len = strlen(xendev->fe); + if (0 != strncmp(xendev->fe, watch, len)) + return; + if (watch[len] != ''/'') + return; + node = watch + len + 1; + + xen_be_frontend_changed(xendev, node); + xen_be_check_state(xendev); +} + +static void xenstore_update(void *unused) +{ + char **vec = NULL; + intptr_t type, ops, ptr; + unsigned int dom, count; + + vec = xs_read_watch(xenstore, &count); + if (NULL == vec) + goto cleanup; + + if (3 == sscanf(vec[XS_WATCH_TOKEN], "be:%" PRIxPTR ":%d:%" PRIxPTR, + &type, &dom, &ops)) + xenstore_update_be(vec[XS_WATCH_PATH], (void*)type, dom, (void*)ops); + if (1 == sscanf(vec[XS_WATCH_TOKEN], "fe:%" PRIxPTR, &ptr)) + xenstore_update_fe(vec[XS_WATCH_PATH], (void*)ptr); + +cleanup: + qemu_free(vec); +} + +static void xen_be_evtchn_event(void *opaque) +{ + struct XenDevice *xendev = opaque; + evtchn_port_t port; + + port = xc_evtchn_pending(xendev->evtchndev); + if (port != xendev->local_port) { + xen_be_printf(xendev, 0, "xc_evtchn_pending returned %d (expected %d)\n", + port, xendev->local_port); + return; + } + xc_evtchn_unmask(xendev->evtchndev, port); + + if (xendev->ops->event) + xendev->ops->event(xendev); +} + +/* -------------------------------------------------------------------- */ + +int xen_be_init(void) +{ + xenstore = xs_daemon_open(); + if (!xenstore) { + fprintf(stderr, "can''t connect to xenstored\n"); + return -1; + } + + if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) + goto err; + + xen_xc = xc_interface_open(); + if (-1 == xen_xc) { + fprintf(stderr, "can''t open xen interface\n"); + goto err; + } + return 0; + +err: + qemu_set_fd_handler(xs_fileno(xenstore), NULL, NULL, NULL); + xs_daemon_close(xenstore); + xenstore = NULL; + + return -1; +} + +int xen_be_register(char *type, struct XenDevOps *ops) +{ + return xenstore_scan(type, xen_domid, ops); +} + +int xen_be_bind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port != -1) + return 0; + xendev->local_port = xc_evtchn_bind_interdomain + (xendev->evtchndev, xendev->dom, xendev->remote_port); + if (-1 == xendev->local_port) { + xendev->local_port = 0; + return -1; + } + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), + xen_be_evtchn_event, NULL, xendev); + return 0; +} + +void xen_be_unbind_evtchn(struct XenDevice *xendev) +{ + if (xendev->local_port == -1) + return; + qemu_set_fd_handler(xc_evtchn_fd(xendev->evtchndev), NULL, NULL, NULL); + xc_evtchn_unbind(xendev->evtchndev, xendev->local_port); + xendev->local_port = -1; +} + +int xen_be_send_notify(struct XenDevice *xendev) +{ + return xc_evtchn_notify(xendev->evtchndev, xendev->local_port); +} + +/* + * msg_level: + * 0 == errors. + * 1 == informative debug messages. + * 2 == noisy debug messages. + * 3 == will flood your log. + */ +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) +{ + va_list args; + + if (msg_level > xendev->debug) + return; + fprintf(stderr, "xen be: %s: ", xendev->name); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} diff --git a/hw/xen-backend.h b/hw/xen-backend.h new file mode 100644 index 0000000..13b3190 --- /dev/null +++ b/hw/xen-backend.h @@ -0,0 +1,87 @@ +#ifndef QEMU_XEN_BACKEND_H +#define QEMU_XEN_BACKEND_H 1 + +#include "xen-common.h" + +/* ------------------------------------------------------------- */ + +#define BUFSIZE 1024 + +struct XenDevice; + +/* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ +#define DEVOPS_FLAG_NEED_GNTDEV 1 +/* don''t expect frontend doing correct state transitions (aka console quirk) */ +#define DEVOPS_FLAG_IGNORE_STATE 2 + +struct XenDevOps { + size_t size; + uint32_t flags; + void (*alloc)(struct XenDevice *xendev); + int (*init)(struct XenDevice *xendev); + int (*connect)(struct XenDevice *xendev); + void (*event)(struct XenDevice *xendev); + void (*disconnect)(struct XenDevice *xendev); + int (*free)(struct XenDevice *xendev); + void (*backend_changed)(struct XenDevice *xendev, const char *node); + void (*frontend_changed)(struct XenDevice *xendev, const char *node); +}; + +struct XenDevice { + char *type; + int dom; + int dev; + char name[64]; + int debug; + + enum xenbus_state be_state; + enum xenbus_state fe_state; + int online; + char be[BUFSIZE]; + char *fe; + char *protocol; + int remote_port; + int local_port; + + int evtchndev; + int gnttabdev; + + struct XenDevOps *ops; + TAILQ_ENTRY(XenDevice) next; +}; + +/* ------------------------------------------------------------- */ + +/* variables */ +extern int xen_domid; /* set by cmd line option, in vl.c */ +extern int xen_xc; +extern struct xs_handle *xenstore; + +/* xenstore helper functions */ +int xenstore_write_str(const char *base, const char *node, const char *val); +int xenstore_write_int(const char *base, const char *node, int ival); +char *xenstore_read_str(const char *base, const char *node); +int xenstore_read_int(const char *base, const char *node, int *ival); + +int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); +int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); +char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); +char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); + +const char *xenbus_strstate(enum xenbus_state state); +struct XenDevice *xen_be_find_xendev(char *type, int dom, int dev); +void xen_be_check_state(struct XenDevice *xendev); + +/* xen backend driver bits */ +int xen_be_init(void); +int xen_be_register(char *type, struct XenDevOps *ops); +int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_unbind_evtchn(struct XenDevice *xendev); +int xen_be_send_notify(struct XenDevice *xendev); +void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) + __attribute__ ((format(printf, 3, 4))); + +#endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-common.h b/hw/xen-common.h new file mode 100644 index 0000000..866049a --- /dev/null +++ b/hw/xen-common.h @@ -0,0 +1,38 @@ +#ifndef QEMU_XEN_COMMON_H +#define QEMU_XEN_COMMON_H 1 + +#include <stddef.h> +#include <inttypes.h> + +#include <xenctrl.h> +#include <xs.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "xen.h" +#include "sys-queue.h" /* BSD list implementation */ + +/* + * tweaks needed to build with different xen versions + * 0x00030205 -> 3.1.0 + * 0x00030207 -> 3.2.0 + * 0x00030208 -> unstable + */ +#include <xen/xen-compat.h> +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030207 +# define xc_map_foreign_pages xc_map_foreign_batch +#endif +#if __XEN_LATEST_INTERFACE_VERSION__ < 0x00030208 +# define xen_mb() mb() +# define xen_rmb() rmb() +# define xen_wmb() wmb() +#endif + +/* ------------------------------------------------------------- */ +/* useful defines */ + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#endif /* QEMU_XEN_COMMON_H */ diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 7757bde..f0ebd60 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -25,7 +25,7 @@ #include "hw.h" #include "boards.h" -#include <xenctrl.h> +#include "xen-backend.h" /* -------------------------------------------------------------------- */ /* variables */ @@ -81,6 +81,11 @@ static int xen_init(void) xen_emulate = 1; } + if (-1 == xen_be_init()) { + fprintf(stderr, "%s: xen backend core setup failed\n", __FUNCTION__); + return -1; + } + return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:50 UTC
[Xen-devel] [PATCH 3/7] xen: add console backend driver.
This patch adds a xenconsole backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 1 + hw/xen-backend.h | 3 + hw/xen-console.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 3 + 4 files changed, 277 insertions(+), 0 deletions(-) create mode 100644 hw/xen-console.c diff --git a/Makefile.target b/Makefile.target index 65f4910..f61458c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,6 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS += xen-console.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 13b3190..61c37ac 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -84,4 +84,7 @@ int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) __attribute__ ((format(printf, 3, 4))); +/* actual backend drivers */ +struct XenDevOps xen_console_ops; /* xen_console.c */ + #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-console.c b/hw/xen-console.c new file mode 100644 index 0000000..57bd3a1 --- /dev/null +++ b/hw/xen-console.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori <aliguori@us.ibm.com> + * + * Copyright (C) Red Hat 2007 + * + * Xen Console + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/select.h> +#include <fcntl.h> +#include <unistd.h> +#include <termios.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <xs.h> +#include <xen/io/console.h> +#include <xenctrl.h> + +#include "hw.h" +#include "sysemu.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__) + +struct buffer { + uint8_t *data; + size_t consumed; + size_t size; + size_t capacity; + size_t max_capacity; +}; + +struct XenConsole { + struct XenDevice xendev; /* must be first */ + struct buffer buffer; + char console[BUFSIZE]; + int ring_ref; + void *sring; + CharDriverState *chr; + int backlog; +}; + +static void buffer_append(struct XenConsole *con) +{ + struct buffer *buffer = &con->buffer; + XENCONS_RING_IDX cons, prod, size; + struct xencons_interface *intf = con->sring; + + cons = intf->out_cons; + prod = intf->out_prod; + xen_mb(); + + size = prod - cons; + if ((size == 0) || (size > sizeof(intf->out))) + return; + + if ((buffer->capacity - buffer->size) < size) { + buffer->capacity += (size + 1024); + buffer->data = realloc(buffer->data, buffer->capacity); + if (buffer->data == NULL) { + dolog(LOG_ERR, "Memory allocation failed"); + exit(ENOMEM); + } + } + + while (cons != prod) + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; + + xen_mb(); + intf->out_cons = cons; + xen_be_send_notify(&con->xendev); + + if (buffer->max_capacity && + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ + + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; + + memmove(maxpos - over, maxpos, over); + buffer->data = realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; + + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; + } +} + +static void buffer_advance(struct buffer *buffer, size_t len) +{ + buffer->consumed += len; + if (buffer->consumed == buffer->size) { + buffer->consumed = 0; + buffer->size = 0; + } +} + +static int ring_free_bytes(struct XenConsole *con) +{ + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX cons, prod, space; + + cons = intf->in_cons; + prod = intf->in_prod; + xen_mb(); + + space = prod - cons; + if (space > sizeof(intf->in)) + return 0; /* ring is screwed: ignore it */ + + return (sizeof(intf->in) - space); +} + +static int xencons_can_receive(void *opaque) +{ + struct XenConsole *con = opaque; + return ring_free_bytes(con); +} + +static void xencons_receive(void *opaque, const uint8_t *buf, int len) +{ + struct XenConsole *con = opaque; + struct xencons_interface *intf = con->sring; + XENCONS_RING_IDX prod; + int i, max; + + max = ring_free_bytes(con); + /* The can_receive() func limits this, but check again anyway */ + if (max < len) + len = max; + + prod = intf->in_prod; + for (i = 0; i < len; i++) { + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] + buf[i]; + } + xen_wmb(); + intf->in_prod = prod; + xen_be_send_notify(&con->xendev); +} + +static void xencons_send(struct XenConsole *con) +{ + ssize_t len, size; + + size = con->buffer.size - con->buffer.consumed; + len = qemu_chr_write(con->chr, con->buffer.data + con->buffer.consumed, + size); + if (len < 1) { + if (!con->backlog) { + con->backlog = 1; + xen_be_printf(&con->xendev, 1, "backlog piling up, nobody listening?\n"); + } + } else { + buffer_advance(&con->buffer, len); + if (con->backlog && len == size) { + con->backlog = 0; + xen_be_printf(&con->xendev, 1, "backlog is gone\n"); + } + } +} + +/* -------------------------------------------------------------------- */ + +static int con_init(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + char *type; + + if (!serial_hds[con->xendev.dev]) { + xen_be_printf(xendev, 1, "serial line %d not configured\n", con->xendev.dev); + return -1; + } + + /* setup */ + snprintf(con->console, sizeof(con->console), + "/local/domain/%d/console", con->xendev.dom); + con->chr = serial_hds[con->xendev.dev]; + + type = xenstore_read_str(con->console, "type"); + if (!type || 0 != strcmp(type, "ioemu")) { + xen_be_printf(xendev, 1, "not for me (type=%s)\n", type); + return -1; + } + + return 0; +} + +static int con_connect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + int limit; + + if (-1 == xenstore_read_int(con->console, "ring-ref", &con->ring_ref)) + return -1; + if (-1 == xenstore_read_int(con->console, "port", &con->xendev.remote_port)) + return -1; + if (0 == xenstore_read_int(con->console, "limit", &limit)) + con->buffer.max_capacity = limit; + + con->sring = xc_map_foreign_range(xen_xc, con->xendev.dom, + XC_PAGE_SIZE, + PROT_READ|PROT_WRITE, + con->ring_ref); + if (!con->sring) + return -1; + + xen_be_bind_evtchn(&con->xendev); + qemu_chr_add_handlers(con->chr, xencons_can_receive, xencons_receive, + NULL, con); + + xen_be_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); + return 0; +} + +static void con_disconnect(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + qemu_chr_add_handlers(con->chr, NULL, NULL, NULL, NULL); + xen_be_unbind_evtchn(&con->xendev); + + if (con->sring) { + munmap(con->sring, XC_PAGE_SIZE); + con->sring = NULL; + } +} + +static void con_event(struct XenDevice *xendev) +{ + struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); + + buffer_append(con); + if (con->buffer.size - con->buffer.consumed) + xencons_send(con); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_console_ops = { + .size = sizeof(struct XenConsole), + .flags = DEVOPS_FLAG_IGNORE_STATE, + .init = con_init, + .connect = con_connect, + .event = con_event, + .disconnect = con_disconnect, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index f0ebd60..de22cdb 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -97,6 +97,9 @@ static int xen_init_pv(DisplayState *ds) if (rc < 0) return rc; + /* xenbus backend drivers */ + xen_be_register("console", &xen_console_ops); + return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:50 UTC
[Xen-devel] [PATCH 4/7] xen: add framebuffer backend driver
This patch adds a frsamebuffer (and kbd+mouse) backend driver. It it based on current xen-unstable code. It has been changed to make use of the common backend driver code. It also has been changed to compile with xen headers older than unstable (aka son-to-be 3.3). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 4 + hw/xen-framebuffer.c | 933 ++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 5 + 4 files changed, 943 insertions(+), 1 deletions(-) create mode 100644 hw/xen-framebuffer.c diff --git a/Makefile.target b/Makefile.target index f61458c..39db580 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o +XEN_OBJS += xen-console.o xen-framebuffer.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 61c37ac..f7dd0c8 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -86,5 +86,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... /* actual backend drivers */ struct XenDevOps xen_console_ops; /* xen_console.c */ +struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ +struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ + +void xen_set_display(int domid, DisplayState *ds); #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-framebuffer.c b/hw/xen-framebuffer.c new file mode 100644 index 0000000..d406c22 --- /dev/null +++ b/hw/xen-framebuffer.c @@ -0,0 +1,933 @@ +/* + * xen paravirt framebuffer backend + * + * Copyright IBM, Corp. 2005-2006 + * Copyright Red Hat, Inc. 2006-2008 + * + * Authors: + * Anthony Liguori <aliguori@us.ibm.com>, + * Markus Armbruster <armbru@redhat.com>, + * Daniel P. Berrange <berrange@redhat.com>, + * Pat Campbell <plc@novell.com>, + * Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdbool.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/event_channel.h> +#include <xen/io/xenbus.h> +#include <xen/io/fbif.h> +#include <xen/io/kbdif.h> +#include <xen/io/protocols.h> + +#include "hw.h" +#include "sysemu.h" +#include "console.h" +#include "qemu-char.h" +#include "xen-backend.h" + +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 /* from <linux/input.h> */ +#endif + +/* -------------------------------------------------------------------- */ + +struct common { + struct XenDevice xendev; /* must be first */ + void *page; + DisplayState *ds; +}; + +struct XenInput { + struct common c; + int abs_pointer_wanted; /* Whether guest supports absolute pointer */ + int button_state; /* Last seen pointer button state */ + int extended; + QEMUPutMouseEntry *qmouse; +}; + +#define UP_QUEUE 8 + +struct XenFB { + struct common c; + size_t fb_len; + int row_stride; + int depth; + int width; + int height; + int offset; + void *pixels; + int feature_update; + int refresh_period; + int bug_trigger; + + struct { + int x,y,w,h; + } up_rects[UP_QUEUE]; + int up_count; + int up_fullscreen; +}; + +/* -------------------------------------------------------------------- */ + +static int common_bind(struct common *c) +{ + int mfn; + + if (-1 == xenstore_read_fe_int(&c->xendev, "page-ref", &mfn)) + return -1; + if (-1 == xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port)) + return -1; + + xen_be_bind_evtchn(&c->xendev); + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n", + mfn, c->xendev.remote_port, c->xendev.local_port); + + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom, + XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, mfn); + if (NULL == c->page) + return -1; + return 0; +} + +static void common_unbind(struct common *c) +{ + if (c->page) { + munmap(c->page, XC_PAGE_SIZE); + c->page = NULL; + } + xen_be_unbind_evtchn(&c->xendev); +} + +/* -------------------------------------------------------------------- */ + +/* + * Tables to map from scancode to Linux input layer keycode. + * Scancodes are hardware-specific. These maps assumes a + * standard AT or PS/2 keyboard which is what QEMU feeds us. + */ +const unsigned char atkbd_set2_keycode[512] = { + + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + +}; + +const unsigned char atkbd_unxlate_table[128] = { + + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13, + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3, + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105, + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63, + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111, + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110 + +}; + +static unsigned char scancode2linux[512]; + +/* Send an event to the keyboard frontend driver */ +static int xenfb_kbd_event(struct XenInput *xenfb, + union xenkbd_in_event *event) +{ + struct xenkbd_page *page = xenfb->c.page; + uint32_t prod; + + if (xenfb->c.xendev.be_state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + xen_mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xen_be_send_notify(&xenfb->c.xendev); +} + +/* Send a keyboard (or mouse button) event */ +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send a relative mouse movement event */ +static int xenfb_send_motion(struct XenInput *xenfb, + int rel_x, int rel_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.motion.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* Send an absolute mouse movement event */ +static int xenfb_send_position(struct XenInput *xenfb, + int abs_x, int abs_y, int rel_z) +{ + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207 + event.pos.rel_z = rel_z; +#endif + + return xenfb_kbd_event(xenfb, &event); +} + +/* + * Send a key event from the client to the guest OS + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard. + * We have to turn this into a Linux Input layer keycode. + * + * Extra complexity from the fact that with extended scancodes + * (like those produced by arrow keys) this method gets called + * twice, but we only want to send a single event. So we have to + * track the ''0xe0'' scancode state & collapse the extended keys + * as needed. + * + * Wish we could just send scancodes straight to the guest which + * already has code for dealing with this... + */ +static void xenfb_key_event(void *opaque, int scancode) +{ + struct XenInput *xenfb = opaque; + int down = 1; + + if (scancode == 0xe0) { + xenfb->extended = 1; + return; + } else if (scancode & 0x80) { + scancode &= 0x7f; + down = 0; + } + if (xenfb->extended) { + scancode |= 0x80; + xenfb->extended = 0; + } + xenfb_send_key(xenfb, down, scancode2linux[scancode]); +} + +/* + * Send a mouse event from the client to the guest OS + * + * The QEMU mouse can be in either relative, or absolute mode. + * Movement is sent separately from button state, which has to + * be encoded as virtual key events. We also don''t actually get + * given any button up/down events, so have to track changes in + * the button state. + */ +static void xenfb_mouse_event(void *opaque, + int dx, int dy, int dz, int button_state) +{ + struct XenInput *xenfb = opaque; + int i; + + if (xenfb->abs_pointer_wanted) + xenfb_send_position(xenfb, + dx * (xenfb->c.ds->width - 1) / 0x7fff, + dy * (xenfb->c.ds->height - 1) / 0x7fff, + dz); + else + xenfb_send_motion(xenfb, dx, dy, dz); + + for (i = 0 ; i < 8 ; i++) { + int lastDown = xenfb->button_state & (1 << i); + int down = button_state & (1 << i); + if (down == lastDown) + continue; + + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0) + return; + } + xenfb->button_state = button_state; +} + +static int input_init(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + static int first = 1; + int i; + + if (first) { + /* Prepare scancode mapping table */ + for (i = 0; i < 128; i++) { + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; + scancode2linux[i | 0x80] + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; + } + first = 0; + } + + if (!in->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + xenstore_write_be_int(xendev, "feature-abs-pointer", 1); + return 0; +} + +static int input_connect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "request-abs-pointer", &in->abs_pointer_wanted)) + in->abs_pointer_wanted = 0; + + rc = common_bind(&in->c); + if (0 != rc) + return rc; + + qemu_add_kbd_event_handler(xenfb_key_event, in); + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in, + in->abs_pointer_wanted, + "Xen PVFB Mouse"); + return 0; +} + +static void input_disconnect(struct XenDevice *xendev) +{ + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); + + if (in->qmouse) { + qemu_remove_mouse_event_handler(in->qmouse); + in->qmouse = NULL; + } + qemu_add_kbd_event_handler(NULL, NULL); + common_unbind(&in->c); +} + +static void input_event(struct XenDevice *xendev) +{ + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); + struct xenkbd_page *page = xenfb->c.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src) +{ + uint32_t *src32 = src; + uint64_t *src64 = src; + int i; + + for (i = 0; i < count; i++) + dst[i] = (mode == 32) ? src32[i] : src64[i]; +} + +static int xenfb_map_fb(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + char *protocol = xenfb->c.xendev.protocol; + int n_fbmfns; + int n_fbdirs; + unsigned long *pgmfns = NULL; + unsigned long *fbmfns = NULL; + void *map, *pd; + int mode, ret = -1; + + /* default to native */ + pd = page->pd; + mode = sizeof(unsigned long) * 8; + + if (!protocol) { + /* + * Undefined protocol, some guesswork needed. + * + * Old frontends which don''t set the protocol use + * one page directory only, thus pd[1] must be zero. + * pd[1] of the 32bit struct layout and the lower + * 32 bits of pd[0] of the 64bit struct layout have + * the same location, so we can check that ... + */ + uint32_t *ptr32 = NULL; + uint32_t *ptr64 = NULL; +#if defined(__i386__) + ptr32 = (void*)page->pd; + ptr64 = ((void*)page->pd) + 4; +#elif defined(__x86_64__) + ptr32 = ((void*)page->pd) - 4; + ptr64 = (void*)page->pd; +#endif + if (ptr32) { + if (0 == ptr32[1]) { + mode = 32; + pd = ptr32; + } else { + mode = 64; + pd = ptr64; + } + } +#if defined(__x86_64__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_32)) { + /* 64bit dom0, 32bit domU */ + mode = 32; + pd = ((void*)page->pd) - 4; +#elif defined(__i386__) + } else if (0 == strcmp(protocol, XEN_IO_PROTO_ABI_X86_64)) { + /* 32bit dom0, 64bit domU */ + mode = 64; + pd = ((void*)page->pd) + 4; +#endif + } + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * mode / 8; + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs); + fbmfns = qemu_mallocz(sizeof(unsigned long) * n_fbmfns); + if (!pgmfns || !fbmfns) + goto out; + + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd); + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ, pgmfns, n_fbdirs); + if (map == NULL) + goto out; + xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map); + munmap(map, n_fbdirs * XC_PAGE_SIZE); + + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pixels == NULL) + goto out; + + ret = 0; /* all is fine */ + +out: + qemu_free(pgmfns); + qemu_free(fbmfns); + return ret; +} + +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, + int width, int height, int depth, + size_t fb_len, int offset, int row_stride) +{ + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; + size_t fb_len_max = fb_pages * XC_PAGE_SIZE; + int max_width, max_height; + + if (fb_len_lim > fb_len_max) { + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n", + fb_len_lim, fb_len_max); + fb_len_lim = fb_len_max; + } + if (fb_len_lim && fb_len > fb_len_lim) { + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n", + fb_len, fb_len_lim); + fb_len = fb_len_lim; + } + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { + xen_be_printf(&xenfb->c.xendev, 0, "can''t handle frontend fb depth %d\n", + depth); + return -1; + } + if (row_stride < 0 || row_stride > fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride); + return -1; + } + max_width = row_stride / (depth / 8); + if (width < 0 || width > max_width) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n", + width, max_width); + width = max_width; + } + if (offset < 0 || offset >= fb_len) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n", + offset, fb_len - 1); + return -1; + } + max_height = (fb_len - offset) / row_stride; + if (height < 0 || height > max_height) { + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n", + height, max_height); + height = max_height; + } + xenfb->fb_len = fb_len; + xenfb->row_stride = row_stride; + xenfb->depth = depth; + xenfb->width = width; + xenfb->height = height; + xenfb->offset = offset; + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n", + width, height, depth, offset, row_stride); + return 0; +} + +/* A convenient function for munging pixels between different depths */ +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \ + for (line = y ; line < (y+h) ; line++) { \ + SRC_T *src = (SRC_T *)(xenfb->pixels \ + + xenfb->offset \ + + (line * xenfb->row_stride) \ + + (x * xenfb->depth / 8)); \ + DST_T *dst = (DST_T *)(xenfb->c.ds->data \ + + (line * xenfb->c.ds->linesize) \ + + (x * xenfb->c.ds->depth / 8)); \ + int col; \ + const int RSS = 32 - (RSB + GSB + BSB); \ + const int GSS = 32 - (GSB + BSB); \ + const int BSS = 32 - (BSB); \ + const uint32_t RSM = (~0U) << (32 - RSB); \ + const uint32_t GSM = (~0U) << (32 - GSB); \ + const uint32_t BSM = (~0U) << (32 - BSB); \ + const int RDS = 32 - (RDB + GDB + BDB); \ + const int GDS = 32 - (GDB + BDB); \ + const int BDS = 32 - (BDB); \ + const uint32_t RDM = (~0U) << (32 - RDB); \ + const uint32_t GDM = (~0U) << (32 - GDB); \ + const uint32_t BDM = (~0U) << (32 - BDB); \ + for (col = x ; col < (x+w) ; col++) { \ + uint32_t spix = *src; \ + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \ + (((spix << GSS) & GSM & GDM) >> GDS) | \ + (((spix << BSS) & BSM & BDM) >> BDS); \ + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \ + dst = (DST_T *) ((unsigned long) dst + xenfb->c.ds->depth / 8); \ + } \ + } + + +/* This copies data from the guest framebuffer region, into QEMU''s copy + * NB. QEMU''s copy is stored in the pixel format of a) the local X + * server (SDL case) or b) the current VNC client pixel format. + * When shifting between colour depths we preserve the MSB. + */ +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h) +{ + int line; + + if (1 /* !xenfb->c.ds->shared_buf */) { + if (xenfb->depth == xenfb->c.ds->depth) { /* Perfect match can use fast path */ + for (line = y ; line < (y+h) ; line++) { + memcpy(xenfb->c.ds->data + (line * xenfb->c.ds->linesize) + (x * xenfb->c.ds->depth / 8), + xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8), + w * xenfb->depth / 8); + } + } else { /* Mismatch requires slow pixel munging */ + /* 8 bit == r:3 g:3 b:2 */ + /* 16 bit == r:5 g:6 b:5 */ + /* 24 bit == r:8 g:8 b:8 */ + /* 32 bit == r:8 g:8 b:8 (padding:8) */ + if (xenfb->depth == 8) { + if (xenfb->c.ds->depth == 16) { + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8); + } + } else if (xenfb->depth == 16) { + if (xenfb->c.ds->depth == 8) { + BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8); + } + } else if (xenfb->depth == 24 || xenfb->depth == 32) { + if (xenfb->c.ds->depth == 8) { + BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2); + } else if (xenfb->c.ds->depth == 16) { + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5); + } else if (xenfb->c.ds->depth == 32) { + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8); + } + } + } + } + dpy_update(xenfb->c.ds, x, y, w, h); +} + +#ifdef XENFB_TYPE_REFRESH_PERIOD +static int xenfb_queue_full(struct XenFB *xenfb) +{ + struct xenfb_page *page = xenfb->c.page; + uint32_t cons, prod; + + prod = page->in_prod; + cons = page->in_cons; + return prod - cons == XENFB_IN_RING_LEN; +} + +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event) +{ + uint32_t prod; + struct xenfb_page *page = xenfb->c.page; + + prod = page->in_prod; + /* caller ensures !xenfb_queue_full() */ + xen_mb(); /* ensure ring space available */ + XENFB_IN_RING_REF(page, prod) = *event; + xen_wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + + xen_be_send_notify(&xenfb->c.xendev); +} + +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period) +{ + union xenfb_in_event event; + + memset(&event, 0, sizeof(event)); + event.type = XENFB_TYPE_REFRESH_PERIOD; + event.refresh_period.period = period; + xenfb_send_event(xenfb, &event); +} +#endif + +/* + * Periodic update of display. + * Also transmit the refresh interval to the frontend. + * + * Never ever do any qemu display operations + * (resize, screen update) outside this function. + * Our screen might be inactive. When asked for + * an update we know it is active. + */ +static void xenfb_update(void *opaque) +{ + struct XenFB *xenfb = opaque; + int i; + + if (xenfb->feature_update) { +#ifdef XENFB_TYPE_REFRESH_PERIOD + int period; + + if (xenfb_queue_full(xenfb)) + return; + + /* + * TODO: xen''s qemu-dm seems to have some patches to + * make the qemu display code avoid unneeded + * work. + * - Port them over. + * - Put ds->idle back into use then. + * - Same goes for ds->shared_buf btw. + */ + if (0 /* xenfb->c.ds->idle */) + period = XENFB_NO_REFRESH; + else { + period = xenfb->c.ds->gui_timer_interval; + if (!period) + period = 30; // GUI_REFRESH_INTERVAL (see vl.c) + } + + if (xenfb->refresh_period != period) { + dprintf("%s: %d\n", __FUNCTION__, period); + xenfb_send_refresh_period(xenfb, period); + xenfb->refresh_period = period; + } +#else + ; /* nothing */ +#endif + } else { + /* we don''t get update notifications, thus use the + * sledge hammer approach ... */ + xenfb->up_fullscreen = 1; + } + + /* resize if needed */ + if (xenfb->width != xenfb->c.ds->width || xenfb->height != xenfb->c.ds->height) { + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing\n"); + dpy_resize(xenfb->c.ds, xenfb->width, xenfb->height); + xenfb->up_fullscreen = 1; + } + + /* run queued updates */ + if (xenfb->up_fullscreen) { + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n"); + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height); + } else if (xenfb->up_count) { + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count); + for (i = 0; i < xenfb->up_count; i++) + xenfb_guest_copy(xenfb, + xenfb->up_rects[i].x, + xenfb->up_rects[i].y, + xenfb->up_rects[i].w, + xenfb->up_rects[i].h); + } else { + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n"); + } + xenfb->up_count = 0; + xenfb->up_fullscreen = 0; +} + +static void xenfb_handle_events(struct XenFB *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->c.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + xen_rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + int x, y, w, h; + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->up_count == UP_QUEUE) + xenfb->up_fullscreen = 1; + if (xenfb->up_fullscreen) + break; + x = MAX(event->update.x, 0); + y = MAX(event->update.y, 0); + w = MIN(event->update.width, xenfb->width - x); + h = MIN(event->update.height, xenfb->height - y); + if (w < 0 || h < 0) { + fprintf(stderr, "xen be: %s: bogus update ignored\n", + xenfb->c.xendev.name); + break; + } + if (x != event->update.x || y != event->update.y + || w != event->update.width + || h != event->update.height) { + fprintf(stderr, "xen be: %s: bogus update clipped\n", + xenfb->c.xendev.name); + } + if (w == xenfb->width && h > xenfb->height / 2) { + /* scroll detector: updated more than 50% of the lines, + * don''t bother keeping track of the rectangles then */ + xenfb->up_fullscreen = 1; + } else { + xenfb->up_rects[xenfb->up_count].x = x; + xenfb->up_rects[xenfb->up_count].y = y; + xenfb->up_rects[xenfb->up_count].w = w; + xenfb->up_rects[xenfb->up_count].h = h; + xenfb->up_count++; + } + break; +#ifdef XENFB_TYPE_RESIZE + case XENFB_TYPE_RESIZE: + if (xenfb_configure_fb(xenfb, xenfb->fb_len, + event->resize.width, + event->resize.height, + event->resize.depth, + xenfb->fb_len, + event->resize.offset, + event->resize.stride) < 0) + break; + xenfb_invalidate(xenfb); + break; +#endif + } + } + xen_mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; +} + +/* QEMU display state changed, so refresh the framebuffer copy */ +static void xenfb_invalidate(void *opaque) +{ + struct XenFB *xenfb = opaque; + xenfb->up_fullscreen = 1; +} + +static int fb_init(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + if (!fb->c.ds) { + /* xen_set_display() below will set that and trigger us again */ + xen_be_printf(xendev, 1, "ds not set (yet)\n"); + return -1; + } + + fb->refresh_period = -1; + +#ifdef XENFB_TYPE_RESIZE + xenstore_write_be_int(xendev, "feature-resize", 1); +#endif + return 0; +} + +static int fb_connect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + struct xenfb_page *fb_page; + int videoram; + int rc; + + if (-1 == xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update)) + fb->feature_update = 0; + if (fb->feature_update) + xenstore_write_be_int(xendev, "request-update", 1); + + if (-1 == xenstore_read_fe_int(xendev, "videoram", &videoram)) + videoram = 0; + + rc = common_bind(&fb->c); + if (0 != rc) + return rc; + + fb_page = fb->c.page; + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + fb_page->width, fb_page->height, fb_page->depth, + fb_page->mem_length, 0, fb_page->line_length); + if (0 != rc) + return rc; + + rc = xenfb_map_fb(fb); + if (0 != rc) + return rc; + + graphic_console_init(fb->c.ds, + xenfb_update, + xenfb_invalidate, + NULL, + NULL, + fb); + + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n", + fb->feature_update, videoram); + + return 0; +} + +static void fb_disconnect(struct XenDevice *xendev) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* FIXME: Hmm, un-init gfx display? can qemu handle that? */ + common_unbind(&fb->c); +} + +static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +{ + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); + + /* + * Set state to Connected *again* once the frontend switched + * to connected. We must trigger the watch a second time to + * workaround a frontend bug. + */ + if (0 == fb->bug_trigger && 0 == strcmp(node, "state") && + xendev->fe_state == XenbusStateConnected && + xendev->be_state == XenbusStateConnected) { + xen_be_set_state(xendev, XenbusStateConnected); + fb->bug_trigger = 1; /* only once */ + } +} + +static void fb_event(struct XenDevice *xendev) +{ + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); + + xenfb_handle_events(xenfb); + xen_be_send_notify(&xenfb->c.xendev); +} + +/* -------------------------------------------------------------------- */ + +struct XenDevOps xen_kbdmouse_ops = { + .size = sizeof(struct XenInput), + .init = input_init, + .connect = input_connect, + .disconnect = input_disconnect, + .event = input_event, +}; + +struct XenDevOps xen_framebuffer_ops = { + .size = sizeof(struct XenFB), + .init = fb_init, + .connect = fb_connect, + .disconnect = fb_disconnect, + .event = fb_event, + .frontend_changed = fb_frontend_changed, +}; + +static void xen_set_display_type(int domid, char *type, DisplayState *ds) +{ + struct XenDevice *xendev; + struct common *c; + + xendev = xen_be_find_xendev(type, domid, 0); + if (!xendev) + return; + c = container_of(xendev, struct common, xendev); + c->ds = ds; + xen_be_printf(xendev, 1, "ds is %p\n", ds); + /* retry ->init() */ + xen_be_check_state(xendev); +} + +void xen_set_display(int domid, DisplayState *ds) +{ + xen_set_display_type(domid, "vkbd", ds); + xen_set_display_type(domid, "vfb", ds); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index de22cdb..8d7140a 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -99,6 +99,11 @@ static int xen_init_pv(DisplayState *ds) /* xenbus backend drivers */ xen_be_register("console", &xen_console_ops); + xen_be_register("vkbd", &xen_kbdmouse_ops); + xen_be_register("vfb", &xen_framebuffer_ops); + + /* setup framebuffer */ + xen_set_display(xen_domid, ds); return 0; } -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:50 UTC
[Xen-devel] [PATCH 5/7] xen: add block device backend driver.
This patch adds a block device backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qdisk" as backend name in xenstore so it doesn''t interfere with the other existing backends (blkback aka "vbd" and tapdisk aka "tap"). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 2 + hw/xen-blkif.h | 103 ++++++++ hw/xen-disk.c | 677 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 1 + sysemu.h | 2 +- vl.c | 4 + 7 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 hw/xen-blkif.h create mode 100644 hw/xen-disk.c diff --git a/Makefile.target b/Makefile.target index 39db580..e668bd2 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index f7dd0c8..7489cbe 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -2,6 +2,7 @@ #define QEMU_XEN_BACKEND_H 1 #include "xen-common.h" +#include "sysemu.h" /* ------------------------------------------------------------- */ @@ -88,6 +89,7 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ... struct XenDevOps xen_console_ops; /* xen_console.c */ struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ +struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-blkif.h b/hw/xen-blkif.h new file mode 100644 index 0000000..254a5fd --- /dev/null +++ b/hw/xen-blkif.h @@ -0,0 +1,103 @@ +#ifndef __XEN_BLKIF_H__ +#define __XEN_BLKIF_H__ + +#include <xen/io/ring.h> +#include <xen/io/blkif.h> +#include <xen/io/protocols.h> + +/* Not a real protocol. Used to generate ring structs which contain + * the elements common to all protocols only. This way we get a + * compiler-checkable way to use common struct elements, so we can + * avoid using switch(protocol) in a number of places. */ +struct blkif_common_request { + char dummy; +}; +struct blkif_common_response { + char dummy; +}; + +/* i386 protocol version */ +#pragma pack(push, 4) +struct blkif_x86_32_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_32_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_32_request blkif_x86_32_request_t; +typedef struct blkif_x86_32_response blkif_x86_32_response_t; +#pragma pack(pop) + +/* x86_64 protocol version */ +struct blkif_x86_64_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t __attribute__((__aligned__(8))) id; + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; +struct blkif_x86_64_response { + uint64_t __attribute__((__aligned__(8))) id; + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; +typedef struct blkif_x86_64_request blkif_x86_64_request_t; +typedef struct blkif_x86_64_response blkif_x86_64_response_t; + +DEFINE_RING_TYPES(blkif_common, struct blkif_common_request, struct blkif_common_response); +DEFINE_RING_TYPES(blkif_x86_32, struct blkif_x86_32_request, struct blkif_x86_32_response); +DEFINE_RING_TYPES(blkif_x86_64, struct blkif_x86_64_request, struct blkif_x86_64_response); + +union blkif_back_rings { + blkif_back_ring_t native; + blkif_common_back_ring_t common; + blkif_x86_32_back_ring_t x86_32; + blkif_x86_64_back_ring_t x86_64; +}; +typedef union blkif_back_rings blkif_back_rings_t; + +enum blkif_protocol { + BLKIF_PROTOCOL_NATIVE = 1, + BLKIF_PROTOCOL_X86_32 = 2, + BLKIF_PROTOCOL_X86_64 = 3, +}; + +static void inline blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +static void inline blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_request_t *src) +{ + int i, n = BLKIF_MAX_SEGMENTS_PER_REQUEST; + + dst->operation = src->operation; + dst->nr_segments = src->nr_segments; + dst->handle = src->handle; + dst->id = src->id; + dst->sector_number = src->sector_number; + if (n > src->nr_segments) + n = src->nr_segments; + for (i = 0; i < n; i++) + dst->seg[i] = src->seg[i]; +} + +#endif /* __XEN_BLKIF_H__ */ diff --git a/hw/xen-disk.c b/hw/xen-disk.c new file mode 100644 index 0000000..96fa9dc --- /dev/null +++ b/hw/xen-disk.c @@ -0,0 +1,677 @@ +/* + * xen paravirt block device backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * FIXME: the code is designed to handle multiple outstanding + * requests (using aio or using threads), which isn''t used right + * now due to limitations of the qemu block driver interface. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <time.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/uio.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> + +#include "hw.h" +#include "block_int.h" +#include "qemu-char.h" +#include "xen-blkif.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +#define BLOCK_SIZE 512 +#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) + +struct ioreq { + blkif_request_t req; + int16_t status; + + /* parsed request */ + off_t start, end; + struct iovec vec[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int vecs; + int presync; + int postsync; + + /* grant mapping */ + uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int prot; + void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + void *pages; + + struct XenBlkDev *blkdev; + LIST_ENTRY(ioreq) list; +}; + +struct XenBlkDev { + struct XenDevice xendev; /* must be first */ + char *params; + char *mode; + char *type; + char *dev; + char *fileproto; + char *filename; + int ring_ref; + void *sring; + int file; + int64_t file_blk; + int64_t file_size; + int protocol; + blkif_back_rings_t rings; + int more_work; + int cnt_map; + + /* request lists */ + LIST_HEAD(inflight_head, ioreq) inflight; + LIST_HEAD(finished_head, ioreq) finished; + LIST_HEAD(freelist_head, ioreq) freelist; + int requests; + + /* qemu block driver */ + BlockDriverState *bs; +}; + +static int syncwrite = 0; +static int batch_maps = 0; +static int max_requests = 32; + +/* ------------------------------------------------------------- */ + +static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq = NULL; + + if (LIST_EMPTY(&blkdev->freelist)) { + if (blkdev->requests >= max_requests) + goto out; + /* allocate new struct */ + ioreq = qemu_mallocz(sizeof(*ioreq)); + if (NULL == ioreq) + goto out; + ioreq->blkdev = blkdev; + blkdev->requests++; + } else { + /* get one from freelist */ + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + } + LIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); + +out: + return ioreq; +} + +static void ioreq_finish(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + LIST_INSERT_HEAD(&blkdev->finished, ioreq, list); +} + +static void ioreq_release(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + + LIST_REMOVE(ioreq, list); + memset(ioreq, 0, sizeof(*ioreq)); + ioreq->blkdev = blkdev; + LIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); +} + +/* + * translate request into iovec + start offset + end offset + * do sanity checks along the way + */ +static int ioreq_parse(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + uintptr_t mem; + size_t len; + int i; + + xen_be_printf(&blkdev->xendev, 3, + "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", + ioreq->req.operation, ioreq->req.nr_segments, + ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + ioreq->prot = PROT_WRITE; /* to memory */ + if (BLKIF_OP_READ != ioreq->req.operation && blkdev->mode[0] != ''w'') { + xen_be_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + goto err; + } + break; + case BLKIF_OP_WRITE_BARRIER: + if (!syncwrite) + ioreq->presync = ioreq->postsync = 1; + /* fall through */ + case BLKIF_OP_WRITE: + ioreq->prot = PROT_READ; /* from memory */ + if (syncwrite) + ioreq->postsync = 1; + break; + default: + xen_be_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + ioreq->req.operation); + goto err; + }; + + ioreq->start = ioreq->end = ioreq->req.sector_number * blkdev->file_blk; + for (i = 0; i < ioreq->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + xen_be_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + goto err; + } + if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { + xen_be_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + goto err; + } + if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { + xen_be_printf(&blkdev->xendev, 0, "error: page crossing\n"); + goto err; + } + len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; + + ioreq->domids[i] = blkdev->xendev.dom; + ioreq->refs[i] = ioreq->req.seg[i].gref; + mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; + + ioreq->vec[i].iov_base = (void*)mem; + ioreq->vec[i].iov_len = len; + ioreq->end += len; + } + if (ioreq->end > blkdev->file_size) { + xen_be_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + goto err; + } + ioreq->vecs = i; + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static void ioreq_unmap(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + if (!ioreq->pages) + return; + if (0 != xc_gnttab_munmap(gnt, ioreq->pages, ioreq->vecs)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map -= ioreq->vecs; + ioreq->pages = NULL; + } else { + for (i = 0; i < ioreq->vecs; i++) { + if (!ioreq->page[i]) + continue; + if (0 != xc_gnttab_munmap(gnt, ioreq->page[i], 1)) + xen_be_printf(&ioreq->blkdev->xendev, 0, "xc_gnttab_munmap failed: %s\n", + strerror(errno)); + ioreq->blkdev->cnt_map--; + ioreq->page[i] = NULL; + } + } +} + +static int ioreq_map(struct ioreq *ioreq) +{ + int gnt = ioreq->blkdev->xendev.gnttabdev; + int i; + + if (batch_maps) { + ioreq->pages = xc_gnttab_map_grant_refs + (gnt, ioreq->vecs, ioreq->domids, ioreq->refs, ioreq->prot); + if (NULL == ioreq->pages) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map %d grant refs (%s, %d maps)\n", + ioreq->vecs, strerror(errno), ioreq->blkdev->cnt_map); + return -1; + } + for (i = 0; i < ioreq->vecs; i++) + ioreq->vec[i].iov_base = ioreq->pages + i * XC_PAGE_SIZE + + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map += ioreq->vecs; + } else { + for (i = 0; i < ioreq->vecs; i++) { + ioreq->page[i] = xc_gnttab_map_grant_ref + (gnt, ioreq->domids[i], ioreq->refs[i], ioreq->prot); + if (NULL == ioreq->page[i]) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "can''t map grant ref %d (%s, %d maps)\n", + ioreq->refs[i], strerror(errno), ioreq->blkdev->cnt_map); + ioreq_unmap(ioreq); + return -1; + } + ioreq->vec[i].iov_base = ioreq->page[i] + (uintptr_t)ioreq->vec[i].iov_base; + ioreq->blkdev->cnt_map++; + } + } + return 0; +} + +static int ioreq_runio_qemu(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int i, rc, len = 0; + off_t pos; + + if (-1 == ioreq_map(ioreq)) + goto err; + if (ioreq->presync) + bdrv_flush(blkdev->bs); + + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_read(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "rd I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_WRITE_BARRIER: + pos = ioreq->start; + for (i = 0; i < ioreq->vecs; i++) { + rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len / BLOCK_SIZE); + if (rc != 0) { + xen_be_printf(&blkdev->xendev, 0, "wr I/O error (%p, len %zd)\n", + ioreq->vec[i].iov_base, + ioreq->vec[i].iov_len); + goto err; + } + len += ioreq->vec[i].iov_len; + pos += ioreq->vec[i].iov_len; + } + break; + default: + /* unknown operation (shouldn''t happen -- parse catches this) */ + goto err; + } + + if (ioreq->postsync) + bdrv_flush(blkdev->bs); + ioreq->status = BLKIF_RSP_OKAY; + + ioreq_unmap(ioreq); + return 0; + +err: + ioreq->status = BLKIF_RSP_ERROR; + return -1; +} + +static int blk_send_response_one(struct ioreq *ioreq) +{ + struct XenBlkDev *blkdev = ioreq->blkdev; + int send_notify = 0; + int have_requests = 0; + blkif_response_t resp; + void *dst; + + resp.id = ioreq->req.id; + resp.operation = ioreq->req.operation; + resp.status = ioreq->status; + + /* Place on the response ring for the relevant domain. */ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + dst = RING_GET_RESPONSE(&blkdev->rings.native, blkdev->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_32, blkdev->rings.x86_32.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + dst = RING_GET_RESPONSE(&blkdev->rings.x86_64, blkdev->rings.x86_64.rsp_prod_pvt); + break; + default: + dst = NULL; + } + memcpy(dst, &resp, sizeof(resp)); + blkdev->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); + if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { + have_requests = 1; + } + + if (have_requests) + blkdev->more_work++; + return send_notify; +} + +/* walk finished list, send outstanding responses, free requests */ +static void blk_send_response_all(struct XenBlkDev *blkdev) +{ + struct ioreq *ioreq; + int send_notify = 0; + + while (!LIST_EMPTY(&blkdev->finished)) { + ioreq = LIST_FIRST(&blkdev->finished); + send_notify += blk_send_response_one(ioreq); + ioreq_release(ioreq); + } + if (send_notify) + xen_be_send_notify(&blkdev->xendev); +} + +static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) +{ + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), + sizeof(ioreq->req)); + break; + case BLKIF_PROTOCOL_X86_32: + blkif_get_x86_32_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_32, rc)); + break; + case BLKIF_PROTOCOL_X86_64: + blkif_get_x86_64_req(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.x86_64, rc)); + break; + } + return 0; +} + +static void blk_handle_requests(struct XenBlkDev *blkdev) +{ + RING_IDX rc, rp; + struct ioreq *ioreq; + + do { + blkdev->more_work = 0; + + rc = blkdev->rings.common.req_cons; + rp = blkdev->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + /* Limit #of requests we queue up for I/O so we ack requests + * faster if busy. Improves backend/frontend parallelism and + * reduces evchn signaling. */ + if (rp > rc + (max_requests >> 2)) { + rp = rc + (max_requests >> 2); + blkdev->more_work++; + } + + while ((rc != rp)) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) + break; + ioreq = ioreq_start(blkdev); + if (NULL == ioreq) { + blkdev->more_work++; + break; + } + blk_get_request(blkdev, ioreq, rc); + blkdev->rings.common.req_cons = ++rc; + + /* parse them */ + if (0 != ioreq_parse(ioreq)) { + if (blk_send_response_one(ioreq)) + xen_be_send_notify(&blkdev->xendev); + ioreq_release(ioreq); + continue; + } + + /* run i/o in qemu mode */ + ioreq_runio_qemu(ioreq); + ioreq_finish(ioreq); + } + blk_send_response_all(blkdev); + + } while (blkdev->more_work); +} + +/* ------------------------------------------------------------- */ + +static void blk_alloc(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + LIST_INIT(&blkdev->inflight); + LIST_INIT(&blkdev->finished); + LIST_INIT(&blkdev->freelist); +} + +static int blk_init(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + int mode, qflags, have_barriers, index, info = 0; + char *h; + + /* read xenstore entries */ + if (NULL == blkdev->params) { + blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + if (NULL != (h = strchr(blkdev->params, '':''))) { + blkdev->fileproto = blkdev->params; + blkdev->filename = h+1; + *h = 0; + } else { + blkdev->fileproto = "<unset>"; + blkdev->filename = blkdev->params; + } + } + if (NULL == blkdev->mode) + blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + if (NULL == blkdev->type) + blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + if (NULL == blkdev->dev) + blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + + /* do we have all we need? */ + if (NULL == blkdev->params || + NULL == blkdev->mode || + NULL == blkdev->type || + NULL == blkdev->dev) + return -1; + + /* read-only ? */ + if (0 == strcmp(blkdev->mode, "w")) { + mode = O_RDWR; + qflags = BDRV_O_RDWR; + } else { + mode = O_RDONLY; + qflags = BDRV_O_RDONLY; + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; + } + + /* init qemu block driver */ + index = (blkdev->xendev.dev - 202 * 256) / 16; + index = drive_get_index(IF_XEN, 0, index); + if (-1 == index) { + blkdev->bs = bdrv_new(blkdev->dev); + if (blkdev->bs) { + if (0 != bdrv_open2(blkdev->bs, blkdev->filename, qflags, + bdrv_find_format(blkdev->fileproto))) { + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + } + if (!blkdev->bs) + return -1; + } else { + blkdev->bs = drives_table[index].bdrv; + } + blkdev->file_blk = BLOCK_SIZE; + blkdev->file_size = bdrv_getlength(blkdev->bs); + if (blkdev->file_size < 0) + blkdev->file_size = 0; + have_barriers = blkdev->bs->drv && blkdev->bs->drv->bdrv_flush ? 1 : 0; + + xen_be_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," + " size %" PRId64 " (%" PRId64 " MB)\n", + blkdev->type, blkdev->fileproto, blkdev->filename, + blkdev->file_size, blkdev->file_size >> 20); + + /* fill info */ + xenstore_write_be_int(&blkdev->xendev, "feature-barrier", have_barriers); + xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int(&blkdev->xendev, "sectors", + blkdev->file_size / blkdev->file_blk); + return 0; +} + +static int blk_connect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "ring-ref", &blkdev->ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&blkdev->xendev, "event-channel", + &blkdev->xendev.remote_port)) + return -1; + + blkdev->protocol = BLKIF_PROTOCOL_NATIVE; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32)) + blkdev->protocol = BLKIF_PROTOCOL_X86_32; + if (0 == strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64)) + blkdev->protocol = BLKIF_PROTOCOL_X86_64; + + blkdev->sring = xc_gnttab_map_grant_ref(blkdev->xendev.gnttabdev, + blkdev->xendev.dom, + blkdev->ring_ref, + PROT_READ | PROT_WRITE); + if (!blkdev->sring) + return -1; + blkdev->cnt_map++; + + switch (blkdev->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.native, sring_native, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_32, sring_x86_32, XC_PAGE_SIZE); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; + BACK_RING_INIT(&blkdev->rings.x86_64, sring_x86_64, XC_PAGE_SIZE); + break; + } + } + + xen_be_bind_evtchn(&blkdev->xendev); + + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " + "remote port %d, local port %d\n", + blkdev->xendev.protocol, blkdev->ring_ref, + blkdev->xendev.remote_port, blkdev->xendev.local_port); + return 0; +} + +static void blk_disconnect(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + + if (-1 != blkdev->file) { + close(blkdev->file); + blkdev->file = -1; + } + if (blkdev->bs) { + bdrv_close(blkdev->bs); + bdrv_delete(blkdev->bs); + blkdev->bs = NULL; + } + xen_be_unbind_evtchn(&blkdev->xendev); + + if (blkdev->sring) { + xc_gnttab_munmap(blkdev->xendev.gnttabdev, blkdev->sring, 1); + blkdev->cnt_map--; + blkdev->sring = NULL; + } +} + +static int blk_free(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + struct ioreq *ioreq; + + while (!LIST_EMPTY(&blkdev->freelist)) { + ioreq = LIST_FIRST(&blkdev->freelist); + LIST_REMOVE(ioreq, list); + qemu_free(ioreq); + } + + qemu_free(blkdev->params); + qemu_free(blkdev->mode); + return 0; +} + +static void blk_event(struct XenDevice *xendev) +{ + struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); + blk_handle_requests(blkdev); +} + +struct XenDevOps xen_blkdev_ops = { + .size = sizeof(struct XenBlkDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .alloc = blk_alloc, + .init = blk_init, + .connect = blk_connect, + .disconnect = blk_disconnect, + .event = blk_event, + .free = blk_free, +}; diff --git a/hw/xen-machine.c b/hw/xen-machine.c index 8d7140a..a1289c1 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -101,6 +101,7 @@ static int xen_init_pv(DisplayState *ds) xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); + xen_be_register("qdisk", &xen_blkdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/sysemu.h b/sysemu.h index b12fae0..49e75b1 100644 --- a/sysemu.h +++ b/sysemu.h @@ -113,7 +113,7 @@ extern unsigned int nb_prom_envs; #endif typedef enum { - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_XEN } BlockInterfaceType; typedef struct DriveInfo { diff --git a/vl.c b/vl.c index c3f2185..a80a46e 100644 --- a/vl.c +++ b/vl.c @@ -5484,6 +5484,9 @@ static int drive_init(struct drive_opt *arg, int snapshot, } else if (!strcmp(buf, "sd")) { type = IF_SD; max_devs = 0; + } else if (!strcmp(buf, "xen")) { + type = IF_XEN; + max_devs = 0; } else { fprintf(stderr, "qemu: ''%s'' unsupported bus type ''%s''\n", str, buf); return -1; @@ -5669,6 +5672,7 @@ static int drive_init(struct drive_opt *arg, int snapshot, switch(type) { case IF_IDE: case IF_SCSI: + case IF_XEN: switch(media) { case MEDIA_DISK: if (cyls != 0) { -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
This patch adds a network interface backend driver to qemu. It is a pure userspace implemention using the gntdev interface. It uses "qnet" as backend name in xenstore so it doesn''t interfere with the netback backend (aka "vnif"). The network backend is hooked into the corrosponding qemu vlan, i.e. vif 0 is hooked into vlan 0. To make the packages actually arrive somewhere you additionally have to link the vlan to the outside world using the usual qemu command line options such as "-net tap,...". Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 1 + hw/xen-machine.c | 1 + hw/xen-nic.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 383 insertions(+), 1 deletions(-) create mode 100644 hw/xen-nic.c diff --git a/Makefile.target b/Makefile.target index e668bd2..85f938c 100644 --- a/Makefile.target +++ b/Makefile.target @@ -517,7 +517,7 @@ endif # xen backend driver support XEN_OBJS := xen-machine.o xen-backend.o -XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o +XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) LIBS += $(XEN_LIBS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index 7489cbe..b484752 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -90,6 +90,7 @@ struct XenDevOps xen_console_ops; /* xen_console.c */ struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */ struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */ struct XenDevOps xen_blkdev_ops; /* xen_disk.c */ +struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); diff --git a/hw/xen-machine.c b/hw/xen-machine.c index a1289c1..c8fb256 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -102,6 +102,7 @@ static int xen_init_pv(DisplayState *ds) xen_be_register("vkbd", &xen_kbdmouse_ops); xen_be_register("vfb", &xen_framebuffer_ops); xen_be_register("qdisk", &xen_blkdev_ops); + xen_be_register("qnic", &xen_netdev_ops); /* setup framebuffer */ xen_set_display(xen_domid, ds); diff --git a/hw/xen-nic.c b/hw/xen-nic.c new file mode 100644 index 0000000..b533a60 --- /dev/null +++ b/hw/xen-nic.c @@ -0,0 +1,380 @@ +/* + * xen paravirt network card backend + * + * (c) Gerd Hoffmann <kraxel@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <linux/if.h> +#include <linux/if_tun.h> + +#include <xs.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/netif.h> + +#include "hw.h" +#include "net.h" +#include "qemu-char.h" +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct XenNetDev { + struct XenDevice xendev; /* must be first */ + char *mac; + int tx_work; + int tx_ring_ref; + int rx_ring_ref; + struct netif_tx_sring *txs; + struct netif_rx_sring *rxs; + netif_tx_back_ring_t tx_ring; + netif_rx_back_ring_t rx_ring; + VLANClientState *vs; +}; + +/* ------------------------------------------------------------- */ + +static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st) +{ + RING_IDX i = netdev->tx_ring.rsp_prod_pvt; + netif_tx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->tx_ring, i); + resp->id = txp->id; + resp->status = st; + +#if 0 + if (txp->flags & NETTXF_extra_info) + RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL; +#endif + + netdev->tx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); + + if (i == netdev->tx_ring.req_cons) { + int more_to_do; + RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do); + if (more_to_do) + netdev->tx_work++; + } +} + +static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end) +{ +#if 0 + /* + * Hmm, why netback fails everything in the ring? + * Should we do that even when not supporting SG and TSO? + */ + RING_IDX cons = netdev->tx_ring.req_cons; + + do { + make_tx_response(netif, txp, NETIF_RSP_ERROR); + if (cons >= end) + break; + txp = RING_GET_REQUEST(&netdev->tx_ring, cons++); + } while (1); + netdev->tx_ring.req_cons = cons; + netif_schedule_work(netif); + netif_put(netif); +#else + net_tx_response(netdev, txp, NETIF_RSP_ERROR); +#endif +} + +static void net_tx_packets(struct XenNetDev *netdev) +{ + netif_tx_request_t txreq; + RING_IDX rc, rp; + void *page; + + for (;;) { + rc = netdev->tx_ring.req_cons; + rp = netdev->tx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + while ((rc != rp)) { + if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) + break; + memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq)); + netdev->tx_ring.req_cons = ++rc; + +#if 1 + /* should not happen in theory, we don''t announce the * + * feature-{sg,gso,whatelse} flags in xenstore (yet?) */ + if (txreq.flags & NETTXF_extra_info) { + xen_be_printf(&netdev->xendev, 0, "FIXME: extra info flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_more_data) { + xen_be_printf(&netdev->xendev, 0, "FIXME: more data flag\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } +#endif + + if (txreq.size < 14) { + xen_be_printf(&netdev->xendev, 0, "bad packet size: %d\n", txreq.size); + net_tx_error(netdev, &txreq, rc); + continue; + } + + if ((txreq.offset + txreq.size) > XC_PAGE_SIZE) { + xen_be_printf(&netdev->xendev, 0, "error: page crossing\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + + xen_be_printf(&netdev->xendev, 3, "tx packet ref %d, off %d, len %d, flags 0x%x%s%s%s%s\n", + txreq.gref, txreq.offset, txreq.size, txreq.flags, + (txreq.flags & NETTXF_csum_blank) ? " csum_blank" : "", + (txreq.flags & NETTXF_data_validated) ? " data_validated" : "", + (txreq.flags & NETTXF_more_data) ? " more_data" : "", + (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + txreq.gref, PROT_READ); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_tx_error(netdev, &txreq, rc); + continue; + } + if (txreq.flags & NETTXF_csum_blank) + net_checksum_calculate(page + txreq.offset, txreq.size); + qemu_send_packet(netdev->vs, page + txreq.offset, txreq.size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); + } + if (!netdev->tx_work) + break; + netdev->tx_work = 0; + } +} + +/* ------------------------------------------------------------- */ + +static void net_rx_response(struct XenNetDev *netdev, + netif_rx_request_t *req, int8_t st, + uint16_t offset, uint16_t size, + uint16_t flags) +{ + RING_IDX i = netdev->rx_ring.rsp_prod_pvt; + netif_rx_response_t *resp; + int notify; + + resp = RING_GET_RESPONSE(&netdev->rx_ring, i); + resp->offset = offset; + resp->flags = flags; + resp->id = req->id; + resp->status = (int16_t)size; + if (st < 0) + resp->status = (int16_t)st; + + xen_be_printf(&netdev->xendev, 3, "rx response: idx %d, status %d, flags 0x%x\n", + i, resp->status, resp->flags); + + netdev->rx_ring.rsp_prod_pvt = ++i; + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify); + if (notify) + xen_be_send_notify(&netdev->xendev); +} + +#define NET_IP_ALIGN 2 + +static int net_rx_ok(void *opaque) +{ + struct XenNetDev *netdev = opaque; + RING_IDX rc, rp; + + if (netdev->xendev.be_state != XenbusStateConnected) + return 0; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "%s: no rx buffers (%d/%d)\n", + __FUNCTION__, rc, rp); + return 0; + } + return 1; +} + +static void net_rx_packet(void *opaque, const uint8_t *buf, int size) +{ + struct XenNetDev *netdev = opaque; + netif_rx_request_t rxreq; + RING_IDX rc, rp; + void *page; + + if (netdev->xendev.be_state != XenbusStateConnected) + return; + + rc = netdev->rx_ring.req_cons; + rp = netdev->rx_ring.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to ''rp''. */ + + if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) { + xen_be_printf(&netdev->xendev, 2, "no buffer, drop packet\n"); + return; + } + if (size > XC_PAGE_SIZE - NET_IP_ALIGN) { + xen_be_printf(&netdev->xendev, 0, "packet too big (%d > %ld)", + size, XC_PAGE_SIZE - NET_IP_ALIGN); + return; + } + + memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); + netdev->rx_ring.req_cons = ++rc; + + page = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + rxreq.gref, PROT_WRITE); + if (NULL == page) { + xen_be_printf(&netdev->xendev, 0, "error: gref dereference failed\n"); + net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0); + return; + } + memcpy(page + NET_IP_ALIGN, buf, size); + xc_gnttab_munmap(netdev->xendev.gnttabdev, page, 1); + net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); +} + +/* ------------------------------------------------------------- */ + +static int net_init(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + VLANState *vlan; + + /* read xenstore entries */ + if (NULL == netdev->mac) + netdev->mac = xenstore_read_be_str(&netdev->xendev, "mac"); + + /* do we have all we need? */ + if (NULL == netdev->mac) + return -1; + + vlan = qemu_find_vlan(netdev->xendev.dev); + netdev->vs = qemu_new_vlan_client(vlan, net_rx_packet, net_rx_ok, netdev); + snprintf(netdev->vs->info_str, sizeof(netdev->vs->info_str), + "nic: xenbus vif macaddr=%s", netdev->mac); + + /* fill info */ + xenstore_write_be_int(&netdev->xendev, "feature-rx-copy", 1); + xenstore_write_be_int(&netdev->xendev, "feature-rx-flip", 0); + + return 0; +} + +static int net_connect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + int rx_copy; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "tx-ring-ref", + &netdev->tx_ring_ref)) + return -1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "rx-ring-ref", + &netdev->rx_ring_ref)) + return 1; + if (-1 == xenstore_read_fe_int(&netdev->xendev, "event-channel", + &netdev->xendev.remote_port)) + return -1; + + if (-1 == xenstore_read_fe_int(&netdev->xendev, "request-rx-copy", &rx_copy)) + rx_copy = 0; + if (0 == rx_copy) { + xen_be_printf(&netdev->xendev, 0, "frontend doesn''t support rx-copy.\n"); + return -1; + } + + netdev->txs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); + netdev->rxs = xc_gnttab_map_grant_ref(netdev->xendev.gnttabdev, + netdev->xendev.dom, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); + if (!netdev->txs || !netdev->rxs) + return -1; + BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XC_PAGE_SIZE); + BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XC_PAGE_SIZE); + + xen_be_bind_evtchn(&netdev->xendev); + + xen_be_printf(&netdev->xendev, 1, "ok: tx-ring-ref %d, rx-ring-ref %d, " + "remote port %d, local port %d\n", + netdev->tx_ring_ref, netdev->rx_ring_ref, + netdev->xendev.remote_port, netdev->xendev.local_port); + return 0; +} + +static void net_disconnect(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + + xen_be_unbind_evtchn(&netdev->xendev); + + if (netdev->txs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->txs, 1); + netdev->txs = NULL; + } + if (netdev->rxs) { + xc_gnttab_munmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + netdev->rxs = NULL; + } +} + +static void net_event(struct XenDevice *xendev) +{ + struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); + net_tx_packets(netdev); +} + +/* ------------------------------------------------------------- */ + +struct XenDevOps xen_netdev_ops = { + .size = sizeof(struct XenNetDev), + .flags = DEVOPS_FLAG_NEED_GNTDEV, + .init = net_init, + .connect = net_connect, + .event = net_event, + .disconnect = net_disconnect, +}; -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 15:50 UTC
[Xen-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line.
This patch makes qemu create backend and frontend device entries in xenstore for devices configured on the command line. It will use qdisk and qnic backend names, so the qemu internal backends will be used. Disks can be created using -drive if=xen,file=... Nics can be created using -net nic,macaddr=... Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- Makefile.target | 2 +- hw/xen-backend.h | 7 +++ hw/xen-config.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/xen-machine.c | 19 +++++++- 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 hw/xen-config.c diff --git a/Makefile.target b/Makefile.target index 85f938c..a3f8d24 100644 --- a/Makefile.target +++ b/Makefile.target @@ -516,7 +516,7 @@ LIBS += $(CONFIG_VNC_TLS_LIBS) endif # xen backend driver support -XEN_OBJS := xen-machine.o xen-backend.o +XEN_OBJS := xen-machine.o xen-backend.o xen-config.o XEN_OBJS += xen-console.o xen-framebuffer.o xen-disk.o xen-nic.o ifeq ($(CONFIG_XEN), yes) OBJS += $(XEN_OBJS) diff --git a/hw/xen-backend.h b/hw/xen-backend.h index b484752..c1da2ca 100644 --- a/hw/xen-backend.h +++ b/hw/xen-backend.h @@ -3,6 +3,8 @@ #include "xen-common.h" #include "sysemu.h" +#include "net.h" +#include "block_int.h" /* ------------------------------------------------------------- */ @@ -94,4 +96,9 @@ struct XenDevOps xen_netdev_ops; /* xen_nic.c */ void xen_set_display(int domid, DisplayState *ds); +/* configuration (aka xenbus setup) */ +void xen_config_cleanup(void); +int xen_config_dev_blk(DriveInfo *disk); +int xen_config_dev_nic(NICInfo *nic); + #endif /* QEMU_XEN_BACKEND_H */ diff --git a/hw/xen-config.c b/hw/xen-config.c new file mode 100644 index 0000000..4156f82 --- /dev/null +++ b/hw/xen-config.c @@ -0,0 +1,139 @@ +#include "xen-backend.h" + +/* ------------------------------------------------------------- */ + +struct xs_dirs { + char *xs_dir; + TAILQ_ENTRY(xs_dirs) list; +}; +static TAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = TAILQ_HEAD_INITIALIZER(xs_cleanup); + +static void xen_config_cleanup_dir(char *dir) +{ + struct xs_dirs *d; + + d = qemu_malloc(sizeof(*d)); + if (!d) + return; + d->xs_dir = dir; + TAILQ_INSERT_TAIL(&xs_cleanup, d, list); +} + +void xen_config_cleanup(void) +{ + struct xs_dirs *d; + + fprintf(stderr, "xen be: %s\n", __FUNCTION__); + TAILQ_FOREACH(d, &xs_cleanup, list) { + xs_rm(xenstore, 0, d->xs_dir); + } +} + +/* ------------------------------------------------------------- */ + +static int xen_config_dev_mkdir(char *dev, int p) +{ + struct xs_permissions perms = { + .id = xen_domid, + .perms = p, + }; + + if (!xs_mkdir(xenstore, 0, dev)) { + fprintf(stderr, "xs_mkdir %s: failed\n", dev); + return -1; + } + xen_config_cleanup_dir(qemu_strdup(dev)); + + if (!xs_set_permissions(xenstore, 0, dev, &perms, 1)) { + fprintf(stderr, "%s: xs_set_permissions failed\n", __FUNCTION__); + return -1; + } + return 0; +} + +static int xen_config_dev_dirs(char *ftype, char *btype, int vdev, + char *fe, char *be, int len) +{ + snprintf(fe, len, "/local/domain/%d/device/%s/%d", + xen_domid, ftype, vdev); + snprintf(be, len, "/local/domain/0/backend/%s/%d/%d", + btype, xen_domid, vdev); + + xen_config_dev_mkdir(fe, XS_PERM_WRITE); + xen_config_dev_mkdir(be, XS_PERM_READ); + return 0; +} + +static int xen_config_dev_all(char *fe, char *be) +{ + /* frontend */ +#if 0 + xenstore_write_str(fe, "protocol", + xen_config_dev_protocol(xen)); +#endif + xenstore_write_int(fe, "state", XenbusStateInitialising); + xenstore_write_int(fe, "backend-id", 0); + xenstore_write_str(fe, "backend", be); + + /* backend */ + xenstore_write_str(be, "domain", qemu_name ? qemu_name : "no-name"); + xenstore_write_int(be, "online", 1); + xenstore_write_int(be, "state", XenbusStateInitialising); + xenstore_write_int(be, "frontend-id", xen_domid); + xenstore_write_str(be, "frontend", fe); + + return 0; +} + +/* ------------------------------------------------------------- */ + +int xen_config_dev_blk(DriveInfo *disk) +{ + char fe[256], be[256]; + int vdev = 202 * 256 + 16 * disk->unit; + int cdrom = disk->bdrv->type == BDRV_TYPE_CDROM; + char *devtype = cdrom ? "cdrom" : "disk"; + char *mode = cdrom ? "r" : "w"; + + snprintf(disk->bdrv->device_name, sizeof(disk->bdrv->device_name), + "xvd%c", ''a'' + disk->unit); + fprintf(stderr, "xen be: config disk %d [%s]: %s\n", + disk->unit, disk->bdrv->device_name, disk->bdrv->filename); + xen_config_dev_dirs("vbd", "qdisk", vdev, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "virtual-device", vdev); + xenstore_write_str(fe, "device-type", devtype); + + /* backend */ + xenstore_write_str(be, "dev", disk->bdrv->device_name); + xenstore_write_str(be, "type", "file"); + xenstore_write_str(be, "params", disk->bdrv->filename); + xenstore_write_str(be, "mode", mode); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} + +int xen_config_dev_nic(NICInfo *nic) +{ + char fe[256], be[256]; + char mac[20]; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + nic->macaddr[0], nic->macaddr[1], nic->macaddr[2], + nic->macaddr[3], nic->macaddr[4], nic->macaddr[5]); + fprintf(stderr, "xen be: config nic %d: mac=\"%s\"\n", nic->vlan->id, mac); + xen_config_dev_dirs("vif", "qnic", nic->vlan->id, fe, be, sizeof(fe)); + + /* frontend */ + xenstore_write_int(fe, "handle", nic->vlan->id); + xenstore_write_str(fe, "mac", mac); + + /* backend */ + xenstore_write_int(be, "handle", nic->vlan->id); + xenstore_write_str(be, "mac", mac); + + /* common stuff */ + return xen_config_dev_all(fe, be); +} diff --git a/hw/xen-machine.c b/hw/xen-machine.c index c8fb256..4e4d893 100644 --- a/hw/xen-machine.c +++ b/hw/xen-machine.c @@ -121,7 +121,7 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, const char *cpu_model) { CPUState *env; - int rc; + int index,i,rc; rc = xen_init_pv(ds); if (-1 == rc) @@ -147,6 +147,23 @@ static void xenpv_init(ram_addr_t ram_size, int vga_ram_size, env = cpu_init(cpu_model); env->halted = 1; + /* configure disks */ + for (i = 0; i < 16; i++) { + index = drive_get_index(IF_XEN, 0, i); + if (index == -1) + continue; + xen_config_dev_blk(drives_table + index); + } + + /* configure nics */ + for (i = 0; i < nb_nics; i++) { + if (nd_table[i].model && 0 != strcmp(nd_table[i].model, "xen")) + continue; + xen_config_dev_nic(nd_table + i); + } + + /* config cleanup hook */ + atexit(xen_config_cleanup); return; err: -- 1.5.5.1 _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-04 17:35 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Gerd Hoffmann wrote:> # OS specific > targetos=`uname -s` > @@ -202,6 +203,7 @@ linux="yes" > linux_user="yes" > if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then > kqemu="yes" > + xen="yes" > audio_possible_drivers="$audio_possible_drivers fmod" > fi >Is this really necessary? The compile check will take care of the non i386/x86_64 cases. It works a lot better when using a cross-compiler too. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-04 17:37 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Blue Swirl, le Mon 04 Aug 2008 20:26:42 +0300, a écrit :> > + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); > > EUGLY_LONG_NAME_WITH_ALL_CAPSThat''s unfortunately the xen API.> Why different protocols for i386 and x86_64?Because the xen block shared structures have been defined at API level, not ABI, so they differ.> Would I need to add Sparc32 and Sparc64 versions some day?Should there be a Xen port to these archs, yes. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-04 17:42 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann wrote:> Hi folks, > > xen support is implemented using another machine type. xen''s qemu-dm > already uses the machine type to switch between paravirtualized and > fully virtualized machines, so this was the natural choice. qemu has > gets a new "xenpv" machine type additionally to the "pc" and "isapc" > ones. > > Comments? >Modulo some of the stylistic feedback that needs to be addressed, I think these series looks pretty good. It fits pretty well into QEMU. Unless there are major objections, I''ll apply an updated series once people have had some time to look through it. However, I''d like to see agreement within the Xen community that this is the right approach first. In particular, I would like to see these patches either merged with upstream Xen or for upstream Xen to plan to rebase against QEMU to pick them up and use them for PV support. This doesn''t mean they have to use the block and net backends of course. I really don''t want to support two implementations of Xen support in QEMU. Regards, Anthony Liguori> cheers, > Gerd > > > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-04 17:46 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: > >> +/* i386 protocol version */ >> +#pragma pack(push, 4) >> > > What''s wrong with __attribute__(__aligned__)? > > >> + * FIXME: the code is designed to handle multiple outstanding >> + * requests (using aio or using threads), which isn''t used right >> + * now due to limitations of the qemu block driver interface. >> > > Yes, what happened to vectored DMA patches? >Believe it or not, they were hurting performance in KVM. Passing a vector to the block layer will force the IO operation to be split up into multiple requests that are all executed in serial (since QEMU only supports 1 outstanding request at a time). Copying the vectored IO to a linear buffer increased performance significantly because it avoided this serialization. The block layer needs some refactoring to support proper vectored AIO operations before a zero-copy API will make sense from a performance perspective. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 18:01 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> - allow xenpv machines run without disk and kernel specified >> by adding a nodisk_ok field to QEMUMachine. > > Nice idea, this would be a useful feature by itself.Yes, some embedded devices probably can use that too according to the source code comment. Guess thats why anthony suggested to do it that way, and I liked the idea too, so I just did ;)>> + $(XEN_OBJS) : CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes > > These aren''t needed for Xen. Though I wonder why at least -Wall > -Wstrict-prototypes could not be added globally.IIRC I tried to turn them on globally and found myself swamped with warnings. So I took the easy way out and enabled them only for the xen bits I''m working on. I like those warnings, they catch sloppyness and help writing clean code. They are in no way required though. We can add them globally nevertheless, I can keep them as local patch, I don''t mind much.>> + if (-1 != xen_present) > > This style looks alien to me. Do you really find it readable?Bad habit. gcc warns these days on "if (foo = 42)", so the reason to write "if (42 = foo)" is gone. I''m still doing that for -- say -- historical reasons? cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 18:04 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 1/7] xen: groundwork for xen support
Anthony Liguori wrote:> Gerd Hoffmann wrote: >> # OS specific >> targetos=`uname -s` >> @@ -202,6 +203,7 @@ linux="yes" >> linux_user="yes" >> if [ "$cpu" = "i386" -o "$cpu" = "x86_64" ] ; then >> kqemu="yes" >> + xen="yes" >> audio_possible_drivers="$audio_possible_drivers fmod" >> fi >> > > Is this really necessary? The compile check will take care of the non > i386/x86_64 cases. It works a lot better when using a cross-compiler too.This is just me being conservative and enable the cases I know it works for. There is ia64 xen ... cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 18:12 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 7/7] xen: blk & nic configuration via cmd line.
Blue Swirl wrote:>> + /* config cleanup hook */ >> + atexit(xen_config_cleanup); > > I''d put this in vl.c, it''s not machine specific.It actually is because it cleans up stuff only created for the xenpv machine type. Moving it to vl.c doesn''t hurt, I would have to wrap it into "#ifdef CONFIG_XEN" though ... cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 18:15 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 3/7] xen: add console backend driver.
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> + buffer->data = realloc(buffer->data, buffer->capacity); > > On Qemu, instead of malloc, qemu_malloc should be used to allocate > memory. Though there isn''t qemu_realloc.Should I add qemu_realloc then?>> + if (!type || 0 != strcmp(type, "ioemu")) { > > Shouldn''t this be "qemu"?No, "ioemu" is correct. xend writes that value into xenstore. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 18:20 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 4/7] xen: add framebuffer backend driver
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> +const unsigned char atkbd_set2_keycode[512] = { > > Missing "static"?Yep, I''ll fix it up.>> + for (i = 0; i < 128; i++) { >> + scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]]; >> + scancode2linux[i | 0x80] >> + atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80]; >> + } > > Can''t these table be calculated so that this initial run-time lookup > is not needed?Should be doable, yes.> The framebuffer does not use dirty memory detection. Is this > intentional? I understand that in Xen that''s not possible.The guest frontend driver does that and sends the info to the backend driver (XENFB_TYPE_UPDATE messages). cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-04 19:50 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> +/* i386 protocol version */ >> +#pragma pack(push, 4) > > What''s wrong with __attribute__(__aligned__)?That one is tricky because we must be binary compatible with xen. And the block driver protocol has a small design flaw: It has a 64bit value which is not aligned on a 64bit border. This leads to different struct layouts on i386 and x86_64 due to different alignment rules. Unless you force them into something else, like we do in that header file to deal with it. Which is needed to run 32bit guests on 64bit hosts.>> + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); > > EUGLY_LONG_NAME_WITH_ALL_CAPSXen API, no way around that, sorry.>> + mode = O_RDONLY; >> + qflags = BDRV_O_RDONLY; >> + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; > > Does this mean that all read-only disks are assumed to be CDROMs or > that all removable disks are read-only?I''ll double-check, but I think there is no media=[disk|cdrom] indicator in xenstore. So that was the best I could come up with ...> Why IF_XEN?I''ve seen you''ve noticed meanwhile in patch #7 ;)> Why different protocols for i386 and x86_64?See above.> Would I need to add > Sparc32 and Sparc64 versions some day?Not sure whenever the same 32/64bit ABI issue exists on sparc. If so, the code handling this on x86 should work for sparc too. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-04 21:34 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Gerd Hoffmann, le Mon 04 Aug 2008 21:50:50 +0200, a écrit :> >> + mode = O_RDONLY; > >> + qflags = BDRV_O_RDONLY; > >> + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; > > > > Does this mean that all read-only disks are assumed to be CDROMs or > > that all removable disks are read-only? > > I''ll double-check, but I think there is no media=[disk|cdrom] indicator > in xenstore.There is: the info node, which is an OR of the VDISK_CDROM/REMOVABLE/READONLY flags. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 06:52 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Samuel Thibault wrote:> Gerd Hoffmann, le Mon 04 Aug 2008 21:50:50 +0200, a écrit : >>>> + mode = O_RDONLY; >>>> + qflags = BDRV_O_RDONLY; >>>> + info |= VDISK_READONLY | VDISK_REMOVABLE | VDISK_CDROM; >>> Does this mean that all read-only disks are assumed to be CDROMs or >>> that all removable disks are read-only? >> I''ll double-check, but I think there is no media=[disk|cdrom] indicator >> in xenstore. > > There is: the info node, which is an OR of the > VDISK_CDROM/REMOVABLE/READONLY flags.Who sets that node? Backend driver or xend? cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 07:01 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Blue Swirl wrote:> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> Blue Swirl wrote: >> > On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >> >> +/* i386 protocol version */ >> >> +#pragma pack(push, 4) >> > >> > What''s wrong with __attribute__(__aligned__)? >> >> >> That one is tricky because we must be binary compatible with xen. And >> the block driver protocol has a small design flaw: It has a 64bit value >> which is not aligned on a 64bit border. This leads to different struct >> layouts on i386 and x86_64 due to different alignment rules. Unless you >> force them into something else, like we do in that header file to deal >> with it. Which is needed to run 32bit guests on 64bit hosts. > > Isn''t this internal Xen protocol, so hopefully next version of Xen > could use more efficient structures?No. It is part of the guest <=> host ABI. So changing that would break existing guest kernels. Otherwise we wouldn''t hop through these loops in the first place. In case the ABI is changed anyway at some point in the future (like it was done from xen 2.x -> 3.0) this can be cleaned up of course. I doubt that will happen though. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 07:18 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Paul Brook wrote:> On Monday 04 August 2008, Gerd Hoffmann wrote: >> Blue Swirl wrote: >>> On 8/4/08, Gerd Hoffmann <kraxel@redhat.com> wrote: >>>> +/* i386 protocol version */ >>>> +#pragma pack(push, 4) >>> What''s wrong with __attribute__(__aligned__)? >> That one is tricky because we must be binary compatible with xen. And >> the block driver protocol has a small design flaw: It has a 64bit value >> which is not aligned on a 64bit border. This leads to different struct >> layouts on i386 and x86_64 due to different alignment rules. Unless you >> force them into something else, like we do in that header file to deal >> with it. Which is needed to run 32bit guests on 64bit hosts. > > qemu should be host independent. You need to use packed structures with > appropriate padding.I think the current code is correct. Ok, the verbose version. The struct in question looks like this: struct blkif_request { uint8_t operation; uint8_t nr_segments; blkif_vdev_t handle; uint64_t id; [ ... ] }; The critical element is id. blkif_vdev_t is uint16_t, which means id is 32bit aligned but not 64bit aligned. On i386 64bit values get 32bit alignments by default. On x86_64 64bit values get 64bit alignments by default. Thus x86_64 has a 32bit padding hole just before id. i386 hasn''t. So we define a i386 and a x86_64 version of the struct. The i386 version is wrapped into a pragma like this ... #pragma pack(push, 4) struct blkif_x86_32_request { [ ... ] } #pragma pack(pop) ... to enforce i386 alignment rules everythere. The x86_64 version looks like this ... struct blkif_x86_64_request { [ ... ] uint64_t __attribute__((__aligned__(8))) id; [ ... ] }; ... to make sure the id element is 64bit aligned everythere. That gives us the correct struct layouts for both i386 and x86_64 builds. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 09:58 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Anthony Liguori wrote:> However, I''d like to see agreement within the Xen community that this > is the right approach first. In particular, I would like to see > these patches either merged with upstream Xen or for upstream Xen to > plan to rebase against QEMU to pick them up and use them for PV > support.Ian Jackson has a qemu git tree at http://www.chiark.greenend.org.uk/~ijackson/qemu-xen.git As far I know it is rebased now and then and the long-term plan is to get the delta to upstream qemu at least smaller. Dunno whenever it ever will be zero due to stuff like mapcache. There are a few compatibility issues to be solved somehow before upstream qemu is able to completely replace xen''s qemu-dm: (1) my patches use -domid to specify the domain id. xen uses (used?) -d instead. Which isn''t going to work because upstream qemu uses that switch for something else. Ian''s tree supports both -d and -domid, with a comment saying -d is deprecated. So I think we have an agreement to use -domid here ;) (2) xen added a -domain-name switch. upstream qemu has a -name switch. my patches do not implement -domain-name, I use the name set via -name instead. IMHO xen should pick up the upstream way to do things here, i.e. switch over to -name. (3) vnc configuration is different. This one is not so easy due to xenstore being involved. So the way things work are pretty xen specific and don''t make that much sense for upstream qemu. The differences are: Setting the vnc password: qemu uses a monitor command. xen reads it from xenstore (set by xend). I think we can just support both ways here. xen has a -vncunused switch which makes qemu scan for a free vnc display. The tcp port actually used is written to xenstore. Dunno how to handle that one best. First, I think should be a -vnc switch option instead of a separate command line switch. Second, propagating the used port via xenstore doesn''t make sense at all for upstream qemu ... cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 10:15 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Tue 05 Aug 2008 11:58:55 +0200, a écrit :> Anthony Liguori wrote: > > However, I''d like to see agreement within the Xen community that this > > is the right approach first. In particular, I would like to see > > these patches either merged with upstream Xen or for upstream Xen to > > plan to rebase against QEMU to pick them up and use them for PV > > support. > > Ian Jackson has a qemu git tree at > http://www.chiark.greenend.org.uk/~ijackson/qemu-xen.git > > As far I know it is rebased now and then and the long-term plan is to > get the delta to upstream qemu at least smaller. Dunno whenever it ever > will be zero due to stuff like mapcache.There are two branches there: the xen branch is stuff that Ian indeed doesn''t intend to push. The qemu branch is stuff that he wishes to push. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 10:46 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Anthony Liguori, le Mon 04 Aug 2008 12:42:38 -0500, a écrit :> However, I''d like to see agreement within the Xen community that this is > the right approach first.Unfortunately, Ian Jackson (who will continue to maintain his qemu tree) is away this week.> In particular, I would like to see these > patches either merged with upstream Xen or for upstream Xen to plan to > rebase against QEMU to pick them up and use them for PV support.I have not had a precise look at e.g. the xenfb.c code, but if what Gerd has submitted is exactly the same as xen-unstable''s, then that part is already merged :) As for the rest, as Gerd said Ian is maintaining a rebased tree, which is currently used by default in the upcoming xen 4.0 (aka 3.3). He won''t rebase it until that release, but after that he will probably regularly, with much less pain than previously anyway since we now have a fine git tree. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 10:47 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Gerd Hoffmann, le Tue 05 Aug 2008 08:52:26 +0200, a écrit :> > There is: the info node, which is an OR of the > > VDISK_CDROM/REMOVABLE/READONLY flags. > > Who sets that node? Backend driver or xend?backend, see linux/driver/xen/blkback/xenbus.c Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 11:07 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Samuel Thibault wrote:> Gerd Hoffmann, le Tue 05 Aug 2008 08:52:26 +0200, a écrit : >>> There is: the info node, which is an OR of the >>> VDISK_CDROM/REMOVABLE/READONLY flags. >> Who sets that node? Backend driver or xend? > > backend, see linux/driver/xen/blkback/xenbus.cHmm, ok. mode="r" (in backend directory) means readonly, ok. device-type="cdrom" (backend dir too) means cdrom, ok. fixed up these. removable can''t be indicated to the backend via xenstore? ignoring that for now. btw: Looks like blktap doesn''t set any VDISK_* flags? cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 11:12 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
> I have not had a precise look at e.g. the xenfb.c code, but if what Gerd > has submitted is exactly the same as xen-unstable''s, then that part is > already merged :)It isn''t exactly the same code. For example the xenbus state engine has been factored out into generic code, shared by all backend drivers. The behaviour should be identical though. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 11:29 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Tue 05 Aug 2008 13:12:35 +0200, a écrit :> > I have not had a precise look at e.g. the xenfb.c code, but if what Gerd > > has submitted is exactly the same as xen-unstable''s, then that part is > > already merged :) > > It isn''t exactly the same code. For example the xenbus state engine has > been factored out into generic code, shared by all backend drivers.Then it''d probably be good to push that upstream Xen. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 11:36 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 5/7] xen: add block device backend driver.
Gerd Hoffmann, le Tue 05 Aug 2008 13:07:52 +0200, a écrit :> Samuel Thibault wrote: > > Gerd Hoffmann, le Tue 05 Aug 2008 08:52:26 +0200, a écrit : > >>> There is: the info node, which is an OR of the > >>> VDISK_CDROM/REMOVABLE/READONLY flags. > >> Who sets that node? Backend driver or xend? > > > > backend, see linux/driver/xen/blkback/xenbus.c > > Hmm, ok. > > mode="r" (in backend directory) means readonly, ok. > device-type="cdrom" (backend dir too) means cdrom, ok.These are set by xend, i.e. when they come from the config, indeed.> removable can''t be indicated to the backend via xenstore?Doesn''t look like it is possible. Why would you want to do that?> btw: Looks like blktap doesn''t set any VDISK_* flags?I guess that''s because blktap doesn''t have the information. The "info" node is information about the underlying source of data, detected by the block backend, while "mode" and "device-type" are configured by the user (and should override the "info" information I guess). Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-05 13:18 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Tue 05 Aug 2008 13:12:35 +0200, a écrit : >>> I have not had a precise look at e.g. the xenfb.c code, but if what Gerd >>> has submitted is exactly the same as xen-unstable''s, then that part is >>> already merged :) >> It isn''t exactly the same code. For example the xenbus state engine has >> been factored out into generic code, shared by all backend drivers. > > Then it''d probably be good to push that upstream Xen.I''m cross-posting the patches to xen-devel for a reason. Whenever you want to pick them up now or just wait for them appear in upstream qemu and handle it with the next rebase is your call. But at least reviewing and commenting now would be very helpful, so I don''t break things for xen by mistake and we can proceed with merging those bits. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 15:03 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Tue 05 Aug 2008 15:18:46 +0200, a écrit :> Samuel Thibault wrote: > > Then it''d probably be good to push that upstream Xen. > > I''m cross-posting the patches to xen-devel for a reason.Well, cross-posting qemu patches to xen-devel is not the same as posting xen patches to xen-devel... Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 15:41 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault, le Tue 05 Aug 2008 16:03:28 +0100, a écrit :> Gerd Hoffmann, le Tue 05 Aug 2008 15:18:46 +0200, a écrit : > > Samuel Thibault wrote: > > > Then it''d probably be good to push that upstream Xen. > > > > I''m cross-posting the patches to xen-devel for a reason. > > Well, cross-posting qemu patches to xen-devel is not the same as posting > xen patches to xen-devel...Just to explain a bit more, now that I have read the patches a bit: what is there is somehow far from the xen unstable tree, at least because of the backend driver core, and because of other ''details'' (renaming, moving). I doubt Ian will be happy to have to make the effort to merge that in his tree and I guess he will probably just drop everything and keep the xen-unstable version. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2008-Aug-05 15:46 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Samuel Thibault, le Tue 05 Aug 2008 16:03:28 +0100, a écrit : > >> Gerd Hoffmann, le Tue 05 Aug 2008 15:18:46 +0200, a écrit : >> >> Well, cross-posting qemu patches to xen-devel is not the same as posting >> xen patches to xen-devel... >> > > Just to explain a bit more, now that I have read the patches a bit: what > is there is somehow far from the xen unstable tree, at least because > of the backend driver core, and because of other ''details'' (renaming, > moving). I doubt Ian will be happy to have to make the effort to merge > that in his tree and I guess he will probably just drop everything and > keep the xen-unstable version. >Which is why ya''ll need to work something out before we apply these patches to QEMU :-) Regards, Anthony Liguori> Samuel >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-05 15:47 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault, le Tue 05 Aug 2008 16:41:40 +0100, a écrit :> Samuel Thibault, le Tue 05 Aug 2008 16:03:28 +0100, a écrit : > > Gerd Hoffmann, le Tue 05 Aug 2008 15:18:46 +0200, a écrit : > > > Samuel Thibault wrote: > > > > Then it''d probably be good to push that upstream Xen. > > > > > > I''m cross-posting the patches to xen-devel for a reason. > > > > Well, cross-posting qemu patches to xen-devel is not the same as posting > > xen patches to xen-devel... > > Just to explain a bit more, now that I have read the patches a bit:And to be even more explicit, by "I have read the patches", I mean: I have applied them to upstream qemu, them vimdiffed the files there with the ones from Ian''s tree (sometimes with different names), in order to have an idea of what the merge would be (i.e. the "xen patches" I mentioned above, which is what we care about from the point of view of xen-unstable). Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 10:14 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Samuel Thibault, le Tue 05 Aug 2008 16:03:28 +0100, a écrit : >> Gerd Hoffmann, le Tue 05 Aug 2008 15:18:46 +0200, a écrit : >>> Samuel Thibault wrote: >>>> Then it''d probably be good to push that upstream Xen. >>> I''m cross-posting the patches to xen-devel for a reason. >> Well, cross-posting qemu patches to xen-devel is not the same as posting >> xen patches to xen-devel... > > Just to explain a bit more, now that I have read the patches a bit: what > is there is somehow far from the xen unstable tree, at least because > of the backend driver core, and because of other ''details'' (renaming, > moving). I doubt Ian will be happy to have to make the effort to merge > that in his tree and I guess he will probably just drop everything and > keep the xen-unstable version.Sure, merging stuff upstream involves alot of work short-term, you''ll get the benefits long-term. pv domain support is easy, as this is largely self-contained. hvm will be even more work, because hvm support is much more invasive and on top of that the qemu interfaces will change to nicely support the various ways to run code natively instead of emulated (kqemu, kvm, xen). The rough way to merge would look like this: - drop xen_console.[ch] - drop xenfb.[ch] - drop xen_machine_pv.c - add xen.h - add xen-machine.c - add xen-backend.[ch] - add xen-console.c - add xen-framebuffer.c - wind up stuff in the Makefiles. - some global renames (domid -> xen_domid for example) as I took care to prefix global xen variables & functions with xen_. - probably some small fixups are needed ... You probably wouldn''t do that before the 3.3 (4.0?) release. If you keep the xenish vl.c version the differences between upstream qemu and qemu-dm command line options and vnc handling go away. The you can gradually move qemu-dm to be more upstream-ish while also updating xend accordingly. Note that qemu-dm has a few optimizations in the qemu display code to avoid unneeded work. I''d suggest to submit them upstream. The we can enable the bits in xen-framebuffer.c which make use of them. DisplayState->idle for example. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 10:23 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 12:14:25 +0200, a écrit :> The rough way to merge would look like this: > > - drop xen_console.[ch] > - drop xenfb.[ch] > - drop xen_machine_pv.c > > - add xen.h > - add xen-machine.c > - add xen-backend.[ch] > - add xen-console.c > - add xen-framebuffer.c > > - wind up stuff in the Makefiles. > - some global renames (domid -> xen_domid for example) as I took care > to prefix global xen variables & functions with xen_. > - probably some small fixups are needed ...You forgot the _test_ stage. You are basically asking us to replace our well-tested implementation with your implementation, that''s quite a move. You are not even providing a patch for us to check that nothing has been left behind...> Note that qemu-dm has a few optimizations in the qemu display code to > avoid unneeded work. I''d suggest to submit them upstream.That''s planned, yes. But you too could do the same for the device backend core for instance...> The you can gradually move qemu-dm to be more upstream-ish while also > updating xend accordingly.That''s what we are already doing in Ian''s tree. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2008-Aug-06 12:43 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault <samuel.thibault@eu.citrix.com> writes:> Gerd Hoffmann, le Wed 06 Aug 2008 12:14:25 +0200, a écrit : >> The rough way to merge would look like this: >> >> - drop xen_console.[ch] >> - drop xenfb.[ch] >> - drop xen_machine_pv.c >> >> - add xen.h >> - add xen-machine.c >> - add xen-backend.[ch] >> - add xen-console.c >> - add xen-framebuffer.c >> >> - wind up stuff in the Makefiles. >> - some global renames (domid -> xen_domid for example) as I took care >> to prefix global xen variables & functions with xen_. >> - probably some small fixups are needed ... > > You forgot the _test_ stage. You are basically asking us to replace our > well-tested implementation with your implementation, that''s quite a > move. You are not even providing a patch for us to check that nothing > has been left behind...It *is* quite move, becauses it accomplishes a lot: it goes from a heavily modified fork of an oldish version all the way to merge with upstream, as far as PV is concerned. If that''s where we want to go, we can of course still argue whether we should go in leaps or baby steps, and whether Gerd''s leap lands in quite the right spot. But the distance to conquer remains the same, and so does the testing challenge. For what it''s worth, I went over significant parts of Gerd''s patch (all the generic stuff + pvfb) with a fine comb, comparing it to what we have now. I consider it sound. [...] _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 12:50 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Markus Armbruster, le Wed 06 Aug 2008 08:43:49 -0400, a écrit :> It *is* quite move, becauses it accomplishes a lot: it goes from a > heavily modified fork of an oldish version all the way to merge with > upstream, as far as PV is concerned.Then why doing it in qemu before having it tested in the xen unstable tree? That''s not the way I usually see merging happen.> For what it''s worth, I went over significant parts of Gerd''s patch > (all the generic stuff + pvfb) with a fine comb, comparing it to what > we have now. I consider it sound.I''m not saying it''s not fine. I had a look and the code looked fine indeed. But what I''m afraid of is the delta with Ian would have to bear when merging: is it save/restore safe, does it work with PCI pass-through, VT-D, etc.?> If that''s where we want to go, we can of course still argue whether we > should go in leaps or baby steps, and whether Gerd''s leap lands in > quite the right spot.Baby steps are much easier to review. That''s how things are usually done, and here it looks to me like it is feasible to achieve in Xen (and have it tested) before event thinking about importing a pile of code in qemu where it won''t receive as much testing as the xen-unstable tree receives. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 13:24 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Hi,> You forgot the _test_ stage. You are basically asking us to replace our > well-tested implementation with your implementation, that''s quite a > move.Sure, and more will follow. I''d expect the merge involves rewriting large parts of the code anyway, especially the hvm bits due to the nessesary interface cleanups in qemu, but also because the code must pass the review process before it can be merged. Pretty much like upstreaming the xen support into the linux kernel. All that also needs testing, no doubt. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 13:39 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 15:24:39 +0200, a écrit :> > You forgot the _test_ stage. You are basically asking us to replace our > > well-tested implementation with your implementation, that''s quite a > > move. > > Sure, and more will follow.But by having it merged into just upstream qemu you won''t magically get Xen people actually test it, so it would be mostly dead code.> Pretty much like upstreaming the xen support into the linux kernel.There''s a big difference here. The upstream linux kernel merge won''t be merged back to the linux-2.6.18 tree. And people do test upstream xen support. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 13:54 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Hi,> Baby steps are much easier to review. That''s how things are usually > done, and here it looks to me like it is feasible to achieve in Xen (and > have it tested) before event thinking about importing a pile of code in > qemu where it won''t receive as much testing as the xen-unstable tree > receives.IMHO the best way to address this issue is to rebase frequently. The xen-unstable testing will also covers qemu then. It also keeps the delta smaller and makes it easier to test qemu patches in qemu-dm. I''m trying to fiddle my bits into qemu-dm right now. One problem is that I get build failures simply because of the fact that the qemu-dm base is old compared to upstream qemu. It would also be great if you can clean up the header mess. qemu-dm doesn''t build without xen-unstable.hg. And just a checked out tree isn''t good enougth, it must be compiled because qemu-dm depends on some generated header files. Aiee. IMHO qemu-dm should only need /usr/include/xen. If headers are missing there, they should be installed. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 14:01 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 15:54:33 +0200, a écrit :> IMHO the best way to address this issue is to rebase frequently.Then why not pushing the changes to xen repositories in the first place?> I''m trying to fiddle my bits into qemu-dm right now. One problem is > that I get build failures simply because of the fact that the qemu-dm > base is old compared to upstream qemu.Use Ian Jackson''s tree!> It would also be great if you can clean up the header mess. qemu-dm > doesn''t build without xen-unstable.hg. And just a checked out tree > isn''t good enougth, it must be compiled because qemu-dm depends on some > generated header files. Aiee. IMHO qemu-dm should only need > /usr/include/xen. If headers are missing there, they should be installed.You could submit patches... Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 14:08 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Wed 06 Aug 2008 15:54:33 +0200, a écrit : >> IMHO the best way to address this issue is to rebase frequently. > > Then why not pushing the changes to xen repositories in the first place?Because I want have xen support in *upstream* qemu.>> I''m trying to fiddle my bits into qemu-dm right now. One problem is >> that I get build failures simply because of the fact that the qemu-dm >> base is old compared to upstream qemu. > > Use Ian Jackson''s tree!Adapt my patches to an oldish qemu fork, so they must be adapted again for submitting upstream? No, thank you. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 14:18 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Wed 06 Aug 2008 15:24:39 +0200, a écrit : >>> You forgot the _test_ stage. You are basically asking us to replace our >>> well-tested implementation with your implementation, that''s quite a >>> move. >> Sure, and more will follow. > > But by having it merged into just upstream qemu you won''t magically get > Xen people actually test it, so it would be mostly dead code. > >> Pretty much like upstreaming the xen support into the linux kernel. > > There''s a big difference here. The upstream linux kernel merge won''t be > merged back to the linux-2.6.18 tree.Oh, we can also arrange things without merging back. Stop rebasing qemu-dm (pretty much like 2.6.18), additionally hook upstream qemu as-is into testing (like pv_ops kernels). Then start submitting patches to qemu. When done, zap old qemu-dm. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 14:25 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 16:08:47 +0200, a écrit :> Samuel Thibault wrote: > > Gerd Hoffmann, le Wed 06 Aug 2008 15:54:33 +0200, a écrit : > >> IMHO the best way to address this issue is to rebase frequently. > > > > Then why not pushing the changes to xen repositories in the first place? > > Because I want have xen support in *upstream* qemu.I know, but you are basically saying "ok, guys, you''ll just manage the breakage of the big merge, thanks", that''s not very convenient, to say the least. For instance, where should fix patches be sent to? Both qemu-devel and xen-devel? Just xen-devel and thus make Ian have to push them to qemu-devel? Pushing the cleaning changes to Xen first can be done and would entail much easier to tackle breakage, and the merge back from qemu would then be trivial, why not doing so?> >> I''m trying to fiddle my bits into qemu-dm right now. One problem is > >> that I get build failures simply because of the fact that the qemu-dm > >> base is old compared to upstream qemu. > > > > Use Ian Jackson''s tree! > > Adapt my patches to an oldish qemu fork, so they must be adapted again > for submitting upstream? No, thank you.AGAIN, THERE IS THE IAN JACKSON TREE. Thanks for reading. Ian has _already_ done that hard work. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 14:51 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 16:18:51 +0200, a écrit :> Samuel Thibault wrote: > > But by having it merged into just upstream qemu you won''t magically get > > Xen people actually test it, so it would be mostly dead code. > > > >> Pretty much like upstreaming the xen support into the linux kernel. > > > > There''s a big difference here. The upstream linux kernel merge won''t be > > merged back to the linux-2.6.18 tree. > > Oh, we can also arrange things without merging back. Stop rebasing > qemu-dm (pretty much like 2.6.18), additionally hook upstream qemu as-is > into testing (like pv_ops kernels). Then start submitting patches to > qemu. When done, zap old qemu-dm.Again, there''s a difference. The interface between Linux and Xen has always been meant to be a "public" thing. The interface between ioemu and the other xen components has not, and changing it is considered fine (we still do it). Sure that could be cleaned up, but that''s probably not something that should happen on the upstream qemu side. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 15:25 UTC
[Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:>> Oh, we can also arrange things without merging back. Stop rebasing >> qemu-dm (pretty much like 2.6.18), additionally hook upstream qemu as-is >> into testing (like pv_ops kernels). Then start submitting patches to >> qemu. When done, zap old qemu-dm. > > Again, there''s a difference. The interface between Linux and Xen has > always been meant to be a "public" thing. The interface between ioemu > and the other xen components has not, and changing it is considered fine > (we still do it). Sure that could be cleaned up, but that''s probably > not something that should happen on the upstream qemu side.Hmm. That makes me think that starting over from scratch really is the best option. Drop the old cruft, design a clean interface, code it up, discuss it in public and submit to upstream qemu. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 15:35 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Wed 06 Aug 2008 16:08:47 +0200, a écrit : >> Samuel Thibault wrote: >>> Gerd Hoffmann, le Wed 06 Aug 2008 15:54:33 +0200, a écrit : >>>> IMHO the best way to address this issue is to rebase frequently. >>> Then why not pushing the changes to xen repositories in the first place? >> Because I want have xen support in *upstream* qemu. > > I know, but you are basically saying "ok, guys, you''ll just manage the > breakage of the big merge, thanks", that''s not very convenient, to say > the least. For instance, where should fix patches be sent to? Both > qemu-devel and xen-devel? Just xen-devel and thus make Ian have to push > them to qemu-devel?Most of the patches probably to both qemu-devel and xen-devel. Stuff unrelated to xen (say e1000 emulation bugs) qemu-devel only.> Pushing the cleaning changes to Xen first can be done and would entail > much easier to tackle breakage, and the merge back from qemu would then > be trivial, why not doing so? > >>>> I''m trying to fiddle my bits into qemu-dm right now. One problem is >>>> that I get build failures simply because of the fact that the qemu-dm >>>> base is old compared to upstream qemu. >>> Use Ian Jackson''s tree! >> Adapt my patches to an oldish qemu fork, so they must be adapted again >> for submitting upstream? No, thank you. > > AGAIN, THERE IS THE IAN JACKSON TREE. > > Thanks for reading.I get build failures when patching THE IAN JACKSON TREE due to THE IAN JACKSON TREE lagging behind upstream. I can read, thank you. Please stop crying.> Ian has _already_ done that hard work.Oh, rebasing isn''t a one-shot thing. You have to do it over and over again. Unless you get your code upstream of course. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 15:47 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Wed 06 Aug 2008 17:35:39 +0200, a écrit :> > AGAIN, THERE IS THE IAN JACKSON TREE. > > > > Thanks for reading. > > I get build failures when patching THE IAN JACKSON TREE due to THE IAN > JACKSON TREE lagging behind upstream.You didn''t mention that. "lagging" a few months indeed, while merging stuff. And because xen 4.0 is about to be released, but it will probably catch up soon. Anyway that''s not a reason for not using it, I guess the build failures can be fixed more easily than redoing Ian''s work.> > Ian has _already_ done that hard work. > > Oh, rebasing isn''t a one-shot thing. You have to do it over and over > again.Sure, that''s why Ian was quite harsh on the modifications to the qemu code, and these are quite small now (I''m talking about the Xen-specific things, non-Xen-specific will be pushed upstream), so that shouldn''t be a problem any more. SAmuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Laurent Vivier
2008-Aug-06 16:01 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Le mercredi 06 août 2008 à 17:35 +0200, Gerd Hoffmann a écrit :> Samuel Thibault wrote:[...]> > AGAIN, THERE IS THE IAN JACKSON TREE. > > > > Thanks for reading. > > I get build failures when patching THE IAN JACKSON TREE due to THE IAN > JACKSON TREE lagging behind upstream. > > I can read, thank you. Please stop crying.Hi guys, if you want to merge your "xen-bits" into qemu, I think you should work together not against each other... (whereas personally I prefer KVM ;-) ) Gerd, IMHO, starting from scratch is never the best solution. Samuel, perhaps you can help Gerd... Regards, Laurent -- ----------------- Laurent.Vivier@bull.net ------------------ "La perfection est atteinte non quand il ne reste rien à ajouter mais quand il ne reste rien à enlever." Saint Exupéry _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-06 22:10 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Wed 06 Aug 2008 17:35:39 +0200, a écrit : >>> AGAIN, THERE IS THE IAN JACKSON TREE. >>> >>> Thanks for reading. >> I get build failures when patching THE IAN JACKSON TREE due to THE IAN >> JACKSON TREE lagging behind upstream. > > You didn''t mention that. "lagging" a few months indeed, while merging > stuff. And because xen 4.0 is about to be released, but it will > probably catch up soon.Well, for 4.0.x you probably have to branch off anyway, so the release shouldn''t stop catching up with upstream.> Anyway that''s not a reason for not using it, I > guess the build failures can be fixed more easily than redoing Ian''s > work.I''m using upstream qemu for development, and I''m not going to change that. I can adapt the patches for the xen tree. Having the xen tree *not* lagging behind would be *very* helpful for keeping the trees and patches in sync. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 22:10 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault, le Wed 06 Aug 2008 15:25:26 +0100, a écrit :> Pushing the cleaning changes to Xen first can be done and would entail > much easier to tackle breakage, and the merge back from qemu would then > be trivial, why not doing so?You didn''t answer that part. Really, my only concern is about having things tested. Isn''t it possible for instance to just merge the backend core (and console/xenfb updates) in Ian''s tree first for a start? As Markus and I said, it looked sane. Then you can push your code to qemu, I guess that could be fine, as you said xen will not need to use e.g. the block and net backends. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-06 22:16 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 00:10:12 +0200, a écrit :> Samuel Thibault wrote: > > Gerd Hoffmann, le Wed 06 Aug 2008 17:35:39 +0200, a écrit : > >>> AGAIN, THERE IS THE IAN JACKSON TREE. > >>> > >>> Thanks for reading. > >> I get build failures when patching THE IAN JACKSON TREE due to THE IAN > >> JACKSON TREE lagging behind upstream. > > > > You didn''t mention that. "lagging" a few months indeed, while merging > > stuff. And because xen 4.0 is about to be released, but it will > > probably catch up soon. > > Well, for 4.0.x you probably have to branch off anyway,When 4.0 gets released, yes.> so the release shouldn''t stop catching up with upstream.The way Xen has been working up to now is to branch the trunk just after the release, not before.> > Anyway that''s not a reason for not using it, I > > guess the build failures can be fixed more easily than redoing Ian''s > > work. > > I''m using upstream qemu for development, and I''m not going to change that.No problem.> I can adapt the patches for the xen tree.That''s what I''m asking you from the start of the thread, I guess my english is not so clear.> Having the xen tree *not* lagging behind would be *very* helpful for > keeping the trees and patches in sync.Sure, it just happens that it couldn''t be done before we get Ian''s tree working. Once the 4.0 release is done we can tighten the gap. And things that shouldn''t be only in Ian''s tree (e.g. the enhanced serial support) are being pushed already. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-07 07:33 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Samuel Thibault, le Wed 06 Aug 2008 15:25:26 +0100, a écrit : >> Pushing the cleaning changes to Xen first can be done and would entail >> much easier to tackle breakage, and the merge back from qemu would then >> be trivial, why not doing so? > > You didn''t answer that part. Really, my only concern is about having > things tested. Isn''t it possible for instance to just merge the backend > core (and console/xenfb updates) in Ian''s tree first for a start?http://kraxel.fedorapeople.org/patches/qemu-xen/ I didn''t touch the build system, it is even more scary than the qemu one alone, I just set CONFIG_XEN unconditionally. I also largely left vl.c as-is, so xend shouldn''t need any changes. The -domid switch sets an additional (redundant) variable, to keep the amount of changes as small as possible for now. Also -name and -domain-name are aliased, both set qemu_name and domain_name. In upstream qemu xenpv support is a runtime-switch for the normal qemu, the xen patches leave the qemu-dm target in place. The framebuffer driver probably has some performance regressions. Fixing those depends on the display patches being pushed upstream.> Then you can push your code to qemu, > I guess that could be fine, as you said xen will not need to use e.g. > the block and net backends.blk and net backends are not there (yet). But they should be a nop for xen anyway as long as you don''t wind up stuff in xend to put them in use. For the net backend it probably wouldn''t be that useful. The block backend should be a good replacement for blktap though and maybe can save you the effort of porting the blktap kernel driver to the pv_ops kernel. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 10:53 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 09:33:06 +0200, a écrit :> Samuel Thibault wrote: > > Samuel Thibault, le Wed 06 Aug 2008 15:25:26 +0100, a écrit : > >> Pushing the cleaning changes to Xen first can be done and would entail > >> much easier to tackle breakage, and the merge back from qemu would then > >> be trivial, why not doing so? > > > > You didn''t answer that part. Really, my only concern is about having > > things tested. Isn''t it possible for instance to just merge the backend > > core (and console/xenfb updates) in Ian''s tree first for a start? > > http://kraxel.fedorapeople.org/patches/qemu-xen/Ok, thanks, that''s already better. I''d still say that "delete a file then add another one" is far from convenient both from a point of view of proof-reading the patches and repository history, but I guess we can manage that with a renaming step first. Any reason for the renames, though? (they tend to bother developpers who have to change their habits, so we can not do that without a reason) - Why dashes instead of underscores? - In Xen, we call xen-machine.c xen_machine_pv.c because there is also a xen_machine_fv.c. Can''t the xen_machine_pv.c name be fine for qemu upstream too? Or xen can keep its own xen_machine_{pv,fv}.c and your xen-machine.c goes upstream, I don''t think that would be a problem. - I guess a xenfb -> xen[-_]{fb,framebuffer} rename is indeed more coherent, Markus, do you prefer just "fb" or the longer "framebuffer"? Misc stuff: - I guess the sys-queue.h file should be merged and used in qemu first? - xen_domid is re-declared in xen-backend.h - The BUFSIZE macro in xen-backend.h is a bit unfortunate, but still better than what we have currently. Maybe it should be renamed with a - XEN_ prefix to avoid any clash? - container_of would probably be useful in other parts of qemu, I guess it could be merged in qemu first? - nice work on moving stuff from console and fb to the backend, console is easier to read now :)> I also largely left vl.c as-is, so xend shouldn''t need any changes. The > -domid switch sets an additional (redundant) variable, to keep the > amount of changes as small as possible for now. Also -name and > -domain-name are aliased, both set qemu_name and domain_name.That''s a good thing :)> The framebuffer driver probably has some performance regressions. > Fixing those depends on the display patches being pushed upstream.It would have been easier to review if you had provided a delta patch, not a delete/add patch </grin>. That''s actually the kind of things I was afraid of and that would have been harder to spot when just pulling your work from qemu upstream. In your xen patch, since idle and GUI_REFRESH_INTERVAL are there, you can just use them. We''ll push them to qemu. We''ll manage the _shared stuff (and push it eventually). I''ll let Markus comment on the rest (again, thanks for moving the xenbus stuff to the backend part). There is one change that is not backend changes or just moving code around: you are queuing rectangle updates, why? (I''m not arguing, just wondering the kind of optimization that can be).> > Then you can push your code to qemu, > > I guess that could be fine, as you said xen will not need to use e.g. > > the block and net backends. > > blk and net backends are not there (yet). But they should be a nop for > xen anywayIndeed, I''d say don''t bother to port those.> The block backend should be a good replacement for blktap though and > maybe can save you the effort of porting the blktap kernel driver to > the pv_ops kernel.Well, for better performance an in-kernel blktap would still be useful. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-07 12:13 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
> Ok, thanks, that''s already better. > > I''d still say that "delete a file then add another one" is far from > convenient both from a point of view of proof-reading the patches and > repository history, but I guess we can manage that with a renaming step > first.Fine with me, I can''t send patches with renames though ...> Any reason for the renames, though? (they tend to bother developpers who > have to change their habits, so we can not do that without a reason)Get consistent naming (all xen stuff in hw/ is prefixed with xen-).> - Why dashes instead of underscores?(1) It looks more pleasent to my eyes. (2) It is easier to type (no shift needed) on both us and german keyboards. (3) The files in the qemu source tree don''t have a consistent style in respect to ''-'' vs. ''_'', so I had no reason to not use my personal preference ;)> - In Xen, we call xen-machine.c xen_machine_pv.c because there is also > a xen_machine_fv.c. Can''t the xen_machine_pv.c name be fine for qemu > upstream too?Dunno whenever there ever will be a xenfv machine type upstream as most likely the normal ''pc'' machine type will get a fancy interface to switch between emulation, kqemu and kvm. And I think the best option for xen hvm would be to hook in there too. That is a long-term thing though, xen_machine_fv.c will probably stay for quite a while at in the xen tree, so maybe ''pv'' in the name helps avoiding confusion. I''d prefer to stay with the xen-machine.c though.> Or xen can keep its own xen_machine_{pv,fv}.c and your > xen-machine.c goes upstream, I don''t think that would be a problem.We have two files defining xenpv_machine then. I don''t think it is a good idea.> Misc stuff: > - I guess the sys-queue.h file should be merged and used in qemu first?Rebase. Then it will be there, and you can skip the patch ;)> - xen_domid is re-declared in xen-backend.hOops. Fixed.> - The BUFSIZE macro in xen-backend.h is a bit unfortunate, but still > better than what we have currently. Maybe it should be renamed with a > - XEN_ prefix to avoid any clash?xen-backend.h is supposed to be included by xen-related code only (it barfs when /usr/include/xen isn''t there), so it shouldn''t clash in theory. Well, I can prefix it nevertheless, better safe than sorry.> - container_of would probably be useful in other parts of qemu, I guess > it could be merged in qemu first?Has qemu a nice place for that kind of helper macros?> In your xen patch, since idle and GUI_REFRESH_INTERVAL are there, you > can just use them. We''ll push them to qemu. We''ll manage the _shared > stuff (and push it eventually).I''d prefer to do it the other way around (push depending changes upstream, then adapt xen-framebuffer.c). I want xen-framebuffer.c look the same in xen and upstream.> I''ll let Markus comment on the rest (again, thanks for moving the xenbus > stuff to the backend part). There is one change that is not backend > changes or just moving code around: you are queuing rectangle updates, > why? (I''m not arguing, just wondering the kind of optimization that can > be).Thats actually a bugfix. Doing screen updates outside the ->update callback isn''t safe. And as we must queuing up update rectangles anyway to fix that it also tries to optimize stuff a bit and avoid unneeded bitblits. Right now that catches only frequent fullscreen updates (such as a scrolling textconsole) and doesn''t copy stuff multiple times in case the update events from the guest are more frequent than update callback calls from qemu. Could be improved to analyze the rectangles in more detail (for example: figure a update is a superset of another one, so one can be dropped).>> The block backend should be a good replacement for blktap though and >> maybe can save you the effort of porting the blktap kernel driver to >> the pv_ops kernel. > > Well, for better performance an in-kernel blktap would still be useful.Don''t confuse blkback and blktap. I don''t want to replace the in-kernel blkback driver. blktap isn''t a in-kernel driver though. It is just an interface between the hypervisor and a userspace application, specialized for block devices. My backend driver does pretty much the same, but uses the generic gntdev interface instead of blktap. I can''t see any reason why it shouldn''t match blktap performance-wise. I have no benchmarks numbers though. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 12:54 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 14:13:22 +0200, a écrit :> > Any reason for the renames, though? (they tend to bother developpers who > > have to change their habits, so we can not do that without a reason) > > Get consistent naming (all xen stuff in hw/ is prefixed with xen-).Err, no, in xen they are all prefixed with xen_ (except xenfb).> > - Why dashes instead of underscores? > > (1) It looks more pleasent to my eyes. > (2) It is easier to type (no shift needed) on both us and german > keyboards.Not on fr keyboard :p> (3) The files in the qemu source tree don''t have a consistent style > in respect to ''-'' vs. ''_'',There are far more _ than - in qemu. - seems to be only used for things that just share a very generic idea (i.e. usb- and scsi-), while _ seems to be used for things that are more closely related, like arm_*, mips_*, ppc_*, ... xen_* would make sense to my mind.> so I had no reason to not use my personal preference ;)Yes, there is a reason: as I said, that puts a little burden on developpers that have already been working on it in Xen for some time. That also asks Ian to do the move, that makes history digging more tricky, etc.> I''d prefer to stay with the xen-machine.c though.Ok for your taste, but as I said the burden involved by renamings looks more important to me.> > Misc stuff: > > - I guess the sys-queue.h file should be merged and used in qemu first? > > Rebase. Then it will be there, and you can skip the patch ;)Ah ok, sorry, then fine.> > - The BUFSIZE macro in xen-backend.h is a bit unfortunate, but still > > better than what we have currently. Maybe it should be renamed with a > > - XEN_ prefix to avoid any clash? > > xen-backend.h is supposed to be included by xen-related code only (it > barfs when /usr/include/xen isn''t there), so it shouldn''t clash in > theory.In theory yes, but even people working on the xen part could want to define a very local BUFSIZE, and then they could get a clash. Of course they can easily fix their code, but as you say> Well, I can prefix it nevertheless, better safe than sorry.> > - container_of would probably be useful in other parts of qemu, I guess > > it could be merged in qemu first? > > Has qemu a nice place for that kind of helper macros?I''ll leave that one for more experienced qemu people.> > In your xen patch, since idle and GUI_REFRESH_INTERVAL are there, you > > can just use them. We''ll push them to qemu. We''ll manage the _shared > > stuff (and push it eventually). > > I''d prefer to do it the other way around (push depending changes > upstream, then adapt xen-framebuffer.c). I want xen-framebuffer.c look > the same in xen and upstream.Sure, it''s just that your code could be merged earlier with these modifications. But well we can handle that later it''s not a problem.> > I''ll let Markus comment on the rest (again, thanks for moving the xenbus > > stuff to the backend part). There is one change that is not backend > > changes or just moving code around: you are queuing rectangle updates, > > why? (I''m not arguing, just wondering the kind of optimization that can > > be). > > Thats actually a bugfix. Doing screen updates outside the ->update > callback isn''t safe.Oh. Well, that''s the kind of comments which you could have put somewhere along your patches, for us to not frown on it while reviewing... For more performance, maybe it''d be better to only move the dpy_update() part. It''s better to do the xenfb_guest_copy() immediately since the source data is probably already hot in the cache.> >> The block backend should be a good replacement for blktap though and > >> maybe can save you the effort of porting the blktap kernel driver to > >> the pv_ops kernel. > > > > Well, for better performance an in-kernel blktap would still be useful. > > Don''t confuse blkback and blktap. I don''t want to replace the in-kernel > blkback driver. blktap isn''t a in-kernel driver though.Well, actually _you_ confused me about this above :)> My backend driver does pretty much the same, but uses the generic > gntdev interface instead of blktap. I can''t see any reason why it > shouldn''t match blktap performance-wise.That''s possible indeed.> I have no benchmarks numbers though.That''d be good :) Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 15:13 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Blue Swirl, le Thu 07 Aug 2008 18:06:07 +0300, a écrit :> After you optimized scancode2linux[], it looks like > atkbd_set2_keycode[] and atkbd_unxlate_table[] are not needed.They should however probably be kept as comments just to know where scancode2linux comes from. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 15:13 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Blue Swirl, le Thu 07 Aug 2008 18:06:07 +0300, a écrit :> 0004-xenpv-groundwork.patch > > You dropped nodisk_ok support from vl.c.I guess that''s ok for a merge in Ian''s tree, which probably has it already in another form. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-07 15:21 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Blue Swirl wrote:>> http://kraxel.fedorapeople.org/patches/qemu-xen/> 0004-xenpv-groundwork.patch > > You dropped nodisk_ok support from vl.c.Oh, there are *two* patchsets: http://kraxel.fedorapeople.org/patches/qemu-upstream/ http://kraxel.fedorapeople.org/patches/qemu-xen/ The first also includes some not-yet submitted work-in-progress bits. It is against upstream qemu svn. The second is against xen''s qemu fork, so the xen-related changes can get a test-drive there. Especially the vl.c changes are very different in the two patch sets because xen''s vl.c looks very different (lot of CONFIG_DM conditional code) and because I don''t want to break the command line interface for now to not break xend''s expectations. That doesn''t mean that the nodisk_ok got lost. It is only in the first patchset though.> 0005-xen-backend-core.patch > > container_of could go to osdep.h.Will do.> 0006-xen-console-backend.patch > > Not your fault, but a lot of places (at least ps2.c and > slavio_serial.c) define some kind of ring buffers. Maybe these could > be consolidated some time.Will have a look if I find some time.> 0007-xen-framebuffer-backend.patch > > After you optimized scancode2linux[], it looks like > atkbd_set2_keycode[] and atkbd_unxlate_table[] are not needed.Indeed. Think I just ifdef them out and leave them in there as documentation. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 16:09 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 14:13:22 +0200, a écrit :> > Or xen can keep its own xen_machine_{pv,fv}.c and your > > xen-machine.c goes upstream, I don''t think that would be a problem. > > We have two files defining xenpv_machine then. I don''t think it is a > good idea.I guess Ian would just drop the xen_machine.c file in his tree. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-07 16:17 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault wrote:> Gerd Hoffmann, le Thu 07 Aug 2008 14:13:22 +0200, a écrit : >>> Any reason for the renames, though? (they tend to bother developpers who >>> have to change their habits, so we can not do that without a reason) >> Get consistent naming (all xen stuff in hw/ is prefixed with xen-). > > Err, no, in xen they are all prefixed with xen_ (except xenfb).Uhm, No. ~/xen/qemu-dm# grep ^OBJS xen-hooks.mak OBJS += piix4acpi.o OBJS += xenstore.o OBJS += xen_platform.o OBJS += xen_machine_fv.o OBJS += xen_machine_fv.o OBJS += xen_blktap.o OBJS += exec-dm.o OBJS += pci_emulation.o OBJS += tpm_tis.o OBJS+= pass-through.o pt-msi.o OBJS := $(filter-out $(BAD_OBJS), $(OBJS)) That is neither consistent wrt using _ everythere nor all files are prefixed consistently. At least all prefixed ones use underscores.>> (3) The files in the qemu source tree don''t have a consistent style >> in respect to ''-'' vs. ''_'', > > There are far more _ than - in qemu. - seems to be only used for > things that just share a very generic idea (i.e. usb- and scsi-), while > _ seems to be used for things that are more closely related, like arm_*, > mips_*, ppc_*, ... xen_* would make sense to my mind.To me it looks pretty random, probably depending on the person creating that file. And when you count them, then there is no clear winner: ~/projects/qemu# find -name "*.[ch]" -print | grep "-" | wc -l 293 ~/projects/qemu# find -name "*.[ch]" -print | grep "_" | wc -l 231>> so I had no reason to not use my personal preference ;) > > Yes, there is a reason: as I said, that puts a little burden on > developpers that have already been working on it in Xen for some time. > That also asks Ian to do the move, that makes history digging more > tricky, etc.git handles renames just fine.> For more performance, maybe it''d be better to only move the dpy_update() > part. It''s better to do the xenfb_guest_copy() immediately since the > source data is probably already hot in the cache.No. The copy is unsafe. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 16:34 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 14:13:22 +0200, a écrit :> > In your xen patch, since idle and GUI_REFRESH_INTERVAL are there, you > > can just use them. We''ll push them to qemu. > > I''d prefer to do it the other way around (push depending changes > upstream, then adapt xen-framebuffer.c). I want xen-framebuffer.c look > the same in xen and upstream.Mmm, looking at that again, most of my idleness patch is actually already upstream. What wasn''t pushed is what is specific to xenfb: transmitting the idleness to the backend. For this xenfb needs to get idleness information. Below is the patch that does that, you can just fold it in your qemu-upstream/0008-xen-add-framebuffer-backend-driver.patch About GUI_REFRESH_INTERVAL, you can just move it to a header. Samuel Index: console.h ==================================================================--- console.h (révision 4992) +++ console.h (copie de travail) @@ -80,6 +80,7 @@ void *opaque; struct QEMUTimer *gui_timer; uint64_t gui_timer_interval; + int idle; void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h); void (*dpy_resize)(struct DisplayState *s, int w, int h); Index: vl.c ==================================================================--- vl.c (révision 4992) +++ vl.c (copie de travail) @@ -5930,6 +5930,8 @@ ds->dpy_update = dumb_update; ds->dpy_resize = dumb_resize; ds->dpy_refresh = dumb_refresh; + ds->gui_timer_interval = 500; + ds->idle = 1; } /***********************************************************/ Index: sdl.c ==================================================================--- sdl.c (révision 4992) +++ sdl.c (copie de travail) @@ -526,9 +526,11 @@ if (ev->active.gain) { /* Back to default interval */ ds->gui_timer_interval = 0; + ds->idle = 0; } else { /* Sleeping interval */ ds->gui_timer_interval = 500; + ds->idle = 1; } } break; Index: vnc.c ==================================================================--- vnc.c (révision 4992) +++ vnc.c (copie de travail) @@ -660,6 +660,7 @@ client_io_error qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; + vs->ds->idle = 1; buffer_reset(&vs->input); buffer_reset(&vs->output); vs->need_update = 0; @@ -1920,6 +1921,7 @@ static void vnc_connect(VncState *vs) { VNC_DEBUG("New client on socket %d\n", vs->csock); + vs->ds->idle = 0; socket_set_nonblock(vs->csock); qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); vnc_write(vs, "RFB 003.008\n", 12); @@ -1959,6 +1961,7 @@ exit(1); ds->opaque = vs; + ds->idle = 1; vnc_state = vs; vs->display = NULL; vs->password = NULL; _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 16:48 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann, le Thu 07 Aug 2008 18:17:39 +0200, a écrit :> Samuel Thibault wrote: > > Gerd Hoffmann, le Thu 07 Aug 2008 14:13:22 +0200, a écrit : > >>> Any reason for the renames, though? (they tend to bother developpers who > >>> have to change their habits, so we can not do that without a reason) > >> Get consistent naming (all xen stuff in hw/ is prefixed with xen-). > > > > Err, no, in xen they are all prefixed with xen_ (except xenfb). > > Uhm, No.Right, there is xenstore as well.> ~/xen/qemu-dm# grep ^OBJS xen-hooks.mak > OBJS += piix4acpi.o > [snip xen*] > OBJS += exec-dm.o > OBJS += pci_emulation.o > OBJS += tpm_tis.o > OBJS+= pass-through.o pt-msi.o > OBJS := $(filter-out $(BAD_OBJS), $(OBJS))These aren''t really xen-specific, that''s why they don''t have a xen or xen_ prefix.> That is neither consistent wrt using _ everythere nor all files are > prefixed consistently. At least all prefixed ones use underscores.And that''s my point. I don''t see why we should take the burden of renaming them with dashes.> >> (3) The files in the qemu source tree don''t have a consistent style > >> in respect to ''-'' vs. ''_'', > > > > There are far more _ than - in qemu.Just to comment on that. I actually meant in hw/ . There are a lot of - in the root, because there are block-*, qemu-*, cpu-*, config-*, etc.> - seems to be only used for > > things that just share a very generic idea (i.e. usb- and scsi-), while > > _ seems to be used for things that are more closely related, like arm_*, > > mips_*, ppc_*, ... xen_* would make sense to my mind. > > To me it looks pretty random,I doesn''t look so much random to me. There are oddities, but the rule above seems mostly respected.> And when you count them, then there is no clear winner: > > ~/projects/qemu# find -name "*.[ch]" -print | grep "-" | wc -l > 293 > ~/projects/qemu# find -name "*.[ch]" -print | grep "_" | wc -l > 231Sure, they have different purpose. As I said, _ for closely related (like must be compiled together), - for not closely related (i.e. independant matter that just have some generic link, like the block interface, scsi or usb bus).> >> so I had no reason to not use my personal preference ;) > > > > Yes, there is a reason: as I said, that puts a little burden on > > developpers that have already been working on it in Xen for some time. > > That also asks Ian to do the move, that makes history digging more > > tricky, etc. > > git handles renames just fine.Yes, sure, that''s what I meant before ("having a renaming step first"). But that''s still work to actually do it, change the Makefiles, and then when you want to git annotate an old version it becomes tricky: you have to remember the old name. So renaming really needs a reason.> > For more performance, maybe it''d be better to only move the dpy_update() > > part. It''s better to do the xenfb_guest_copy() immediately since the > > source data is probably already hot in the cache. > > No. The copy is unsafe.Ah, because we''re writing to ds->data which is handled by the display backend, right. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-07 16:54 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Samuel Thibault, le Thu 07 Aug 2008 17:48:43 +0100, a écrit :> > OBJS += exec-dm.o > > These aren''t really xen-specific, that''s why they don''t have a xen or > xen_ prefix.Except this one of course since it''s the way it''s supposed to be called. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stefano Stabellini
2008-Aug-07 17:40 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann wrote:> It would also be great if you can clean up the header mess. qemu-dm > doesn''t build without xen-unstable.hg. And just a checked out tree > isn''t good enougth, it must be compiled because qemu-dm depends on some > generated header files. Aiee. IMHO qemu-dm should only need > /usr/include/xen. If headers are missing there, they should be installed. >It is not difficult to make Ian''s qemu tree compile against /usr/include/xen. Expect a patch to fix this on Monday. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2008-Aug-08 08:01 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
On 7/8/08 18:40, "Stefano Stabellini" <stefano.stabellini@eu.citrix.com> wrote:> Gerd Hoffmann wrote: > >> It would also be great if you can clean up the header mess. qemu-dm >> doesn''t build without xen-unstable.hg. And just a checked out tree >> isn''t good enougth, it must be compiled because qemu-dm depends on some >> generated header files. Aiee. IMHO qemu-dm should only need >> /usr/include/xen. If headers are missing there, they should be installed. >> > > It is not difficult to make Ian''s qemu tree compile against > /usr/include/xen. > Expect a patch to fix this on Monday.[cc list stripped] It goes without saying that this shouldn''t be the default when building out of the Xen repository using our build system. Not everyone wants to install our headers on the build machine. But making /usr/include the default and having Xen''s build system override at configure time would be fine. -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stefano Stabellini
2008-Aug-08 09:29 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Keir Fraser wrote:> On 7/8/08 18:40, "Stefano Stabellini" <stefano.stabellini@eu.citrix.com> > wrote: > >> Gerd Hoffmann wrote: >> >>> It would also be great if you can clean up the header mess. qemu-dm >>> doesn''t build without xen-unstable.hg. And just a checked out tree >>> isn''t good enougth, it must be compiled because qemu-dm depends on some >>> generated header files. Aiee. IMHO qemu-dm should only need >>> /usr/include/xen. If headers are missing there, they should be installed. >>> >> It is not difficult to make Ian''s qemu tree compile against >> /usr/include/xen. >> Expect a patch to fix this on Monday. > > [cc list stripped] > > It goes without saying that this shouldn''t be the default when building out > of the Xen repository using our build system. Not everyone wants to install > our headers on the build machine. But making /usr/include the default and > having Xen''s build system override at configure time would be fine. >Yes, that was the idea. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Samuel Thibault
2008-Aug-08 11:03 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Blue Swirl, le Thu 07 Aug 2008 18:06:07 +0300, a écrit :> 0006-xen-console-backend.patch > > Not your fault, but a lot of places (at least ps2.c and > slavio_serial.c) define some kind of ring buffers. Maybe these could > be consolidated some time.There is also one in console.c It''d be probably be good to have an implementation of a ring of arbitrary structures. We have a pending patch of usb-hid to fix double clicks by queueing click events. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-11 08:16 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Hi,> Mmm, looking at that again, most of my idleness patch is actually > already upstream. What wasn''t pushed is what is specific to xenfb: > transmitting the idleness to the backend. For this xenfb needs to get > idleness information. Below is the patch that does that, you can just > fold it in your qemu-upstream/0008-xen-add-framebuffer-backend-driver.patch > About GUI_REFRESH_INTERVAL, you can just move it to a header.Thanks, I''ll stick it into my patch queue. That leaves the DisplayState->shared_buf bits which should be upstreamed too ... cheers, Gerd _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ian Jackson
2008-Aug-11 09:18 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann writes ("Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu"):> IMHO the best way to address this issue is to rebase frequently. The > xen-unstable testing will also covers qemu then. It also keeps the > delta smaller and makes it easier to test qemu patches in qemu-dm.I''m not going to be rebasing, but I will merge instead.> I''m trying to fiddle my bits into qemu-dm right now. One problem is > that I get build failures simply because of the fact that the qemu-dm > base is old compared to upstream qemu.Yes, that''s currently the case because qemu-xen-unstable is frozen for the forthcoming Xen release.> It would also be great if you can clean up the header mess. qemu-dm > doesn''t build without xen-unstable.hg.This is true and is likely to continue to be the case indefinitely.> And just a checked out tree > isn''t good enougth, it must be compiled because qemu-dm depends on some > generated header files. Aiee. IMHO qemu-dm should only need > /usr/include/xen. If headers are missing there, they should be installed.qemu-dm also needs libxc, libthis, libthat from xen-unstable. Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stefano Stabellini
2008-Aug-11 09:23 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann wrote:> Hi, > >> Mmm, looking at that again, most of my idleness patch is actually >> already upstream. What wasn''t pushed is what is specific to xenfb: >> transmitting the idleness to the backend. For this xenfb needs to get >> idleness information. Below is the patch that does that, you can just >> fold it in your qemu-upstream/0008-xen-add-framebuffer-backend-driver.patch >> About GUI_REFRESH_INTERVAL, you can just move it to a header. > > Thanks, I''ll stick it into my patch queue. > > That leaves the DisplayState->shared_buf bits which should be upstreamed > too ... >It is the next item on my TODO :) _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ian Jackson
2008-Aug-11 10:07 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Keir Fraser writes ("Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu"):> It goes without saying that this shouldn''t be the default when building out > of the Xen repository using our build system. Not everyone wants to install > our headers on the build machine. But making /usr/include the default and > having Xen''s build system override at configure time would be fine.I agree that having this as a build option is a good idea. I think it would be better not to have it be the default, though. Autobuilder systems typically want to avoid accidentally picking up stuff from the build system environment, so that builds stay reproducible. Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ian Jackson
2008-Aug-11 10:12 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Gerd Hoffmann writes ("Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu"):> [Samuel asks:] > > - Why dashes instead of underscores? > > (1) It looks more pleasent to my eyes. > (2) It is easier to type (no shift needed) on both us and german > keyboards. > (3) The files in the qemu source tree don''t have a consistent style > in respect to ''-'' vs. ''_'', so I had no reason to not use my > personal preference ;)I basically agree with this. However:> > Any reason for the renames, though? (they tend to bother developpers who > > have to change their habits, so we can not do that without a reason) > > Get consistent naming (all xen stuff in hw/ is prefixed with xen-).I absolutely and vehemently disagree that files should be renamed (or indeed code style changed) to make things prettier or more consistent. There are currently at least three substantial branches and numerous smaller branches of this xen code in at least two different revision control systems - and there are probably many more. The very last thing we want is to make porting patches between the various branches difficult. Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-11 11:06 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Ian Jackson wrote:> Keir Fraser writes ("Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu"): >> It goes without saying that this shouldn''t be the default when building out >> of the Xen repository using our build system. Not everyone wants to install >> our headers on the build machine. But making /usr/include the default and >> having Xen''s build system override at configure time would be fine. > > I agree that having this as a build option is a good idea. > > I think it would be better not to have it be the default, though.It should be the default for the git tree, so it can be checked out and built as-is without having to pull more stuff. The xen build system can easily override it when it builds qemu-dm as part of a complete xen build.> Autobuilder systems typically want to avoid accidentally picking up > stuff from the build system environment, so that builds stay > reproducible.... or they prepare the environment the way they want it. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-11 11:08 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Ian Jackson wrote:>> And just a checked out tree >> isn''t good enougth, it must be compiled because qemu-dm depends on some >> generated header files. Aiee. IMHO qemu-dm should only need >> /usr/include/xen. If headers are missing there, they should be installed. > > qemu-dm also needs libxc, libthis, libthat from xen-unstable.That kind of stuff is usually packed up as xen-devel in linux distributions. I want being able to just install xen-devel on my machine, then compile qemu with xen support. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-11 12:46 UTC
Re: [Xen-devel] Re: [Qemu-devel] [PATCH 0/7] merge some xen bits into qemu
Blue Swirl wrote:> I extracted a small patch that only introduces the nodisk_ok part. > Anybody unhappy if I commit it?How about converting all struct fields to C99 initializers in hw/sun4*.c? Mixing the two styles looks quite odd. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
I thought that following that lovely discussion it might be helpful to summarise a few things about the way I see things: As Samuel says, I''m currently maintaining two branches: one contains code which I would like to get to upstream qemu in the nearish future, and one which contains changes which I don''t think have a practical chance of going upstream any time soon. (I also have a gaggle of topic branches of course.) Because Xen 3.3 is nearing release and currently firmly frozen, both of these branches are currently based on a few months'' old version of qemu upstream. I intend to fork off new development branches of both very soon, and then bring both of them up to parity with a modern qemu upstream. One natural output of such a merge process is a number of patches for upstream. Once that merge is done I intend, while Xen development is fully open, to frequently update both my new trees from upstream - say, weekly. As before, shortly before the Xen freeze I will stop pulling from qemu upstream. It would be very nice to get the differences between qemu in Xen and qemu upstream down to zero. There are three kinds of change in our tree which will prevent this in the short to medium term: Firstly, there are some things in our tree which are simply too ugly. We would like to improve them and feed them upstream in due course, but that''s more of a medium to long term project. A top priority in this area is display handling. Secondly, there are changes which are only useful in a Xen context. An example would be the machinery to monitor `disk change'' events which are injected via the Xen management tools. This is done in our qemu by xen-specific code which talks to the xenstore IPC/IDC daemon. This code is completely Xen specific and depends on the presence of the Xen management stack, so it must only be compiled in to the specific Xen device model qemu executable. We would be happy to have this code in qemu upstream, but it is quite closely linked to much of the Xen management toolstack and quite loosely coupled to the rest of qemu so in practice this is not currently a priority for us. Thirdly, there are some changes which we have been told are not suitable for qemu upstream, but which we (Xen) are pretty convinced we need to retain. An example is the mapcache. (Please don''t explain again why the mapcache is not necessary. That conversation has been had and we''ve come to different conclusions, which is fine - this is Free Software after all, which means that we can work together well, and hopefully without rancour, even though we sometimes disagree.) There are also another set of concievable things: _emulations_ in qemu of the Xen environment, so that a Xen PV guest can be run without the Xen hypervisor and host infrastructure, or so that Xen PV drivers can be used to improve the performance of a plain qemu guest. That includes, for example, xenstore/xenbus emulation, Xen PV backend drivers, and the like. Gerd''s patches contained several such things. I think a qemu emulator which can support Xen guests without needing Xen is in principle a fine and good thing to have in qemu upstream. We don''t have it in our Xen tree because we have the real hypervisor and dom0-provided backends. But I''m sure we''d find them useful for debugging. BUT we have to not get confused so the names must be different. It is necessary to clearly distinguish these two use cases: * qemu-dm run under the Xen hypervisor, xend, with xenstore, to support Xen guests as currently done in the Xen upstream project * qemu emulating the Xen environment I think these should be given different names. We should not use `xen'' for both. The plumbing involved is quite different. Users will get confused and the results will be mysterious failures. Developers will get confused and the results will be people talking and coding past each other. I would like to suggest that the first of these two options should be called `xen'' or `xen-hvm'' or `xen-pv'' or `xen-fv'' or some such. Code in qemu intended to support running under the Xen hypervisor and upstream Xen toolstack should use the name `xen''. This includes (for example) portability to the Xen minios embedded system, frontend drivers for Xen PV block and network devices, interaction with xenstore, interaction with the hypervisor to collect IO requests from the running guest and return the responses, and so on. Code in qemu intended to support Xen guests without the use of the Xen hypervisor and tools should be called `xenemu'' or some such. This includes backend drivers for block and network devices, xenstore/xenbus emulation, and so on. Ian. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ian Jackson, le Mon 11 Aug 2008 17:36:22 +0100, a écrit :> Code in qemu intended to support Xen guests without the use of the Xen > hypervisor and tools should be called `xenemu'' or some such. This > includes backend drivers for block and network devices, > xenstore/xenbus emulation, and so on.Mmm, for backends it would be a bit odd, since in xen upstream we do have backends too (console and framebuffer), and as Gerd said we could in theory use the block and network backends too. Samuel _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Hi Ian, Ian Jackson wrote:> Because Xen 3.3 is nearing release and currently firmly frozen, both > of these branches are currently based on a few months'' old version of > qemu upstream. I intend to fork off new development branches of both > very soon, and then bring both of them up to parity with a modern qemu > upstream. One natural output of such a merge process is a number of > patches for upstream. >I''m looking forward to this and appreciate your work here.> It would be very nice to get the differences between qemu in Xen and > qemu upstream down to zero. There are three kinds of change in our > tree which will prevent this in the short to medium term: > > Firstly, there are some things in our tree which are simply too ugly. > We would like to improve them and feed them upstream in due course, > but that''s more of a medium to long term project. A top priority in > this area is display handling. > > Secondly, there are changes which are only useful in a Xen context. > An example would be the machinery to monitor `disk change'' events > which are injected via the Xen management tools. This is done in our > qemu by xen-specific code which talks to the xenstore IPC/IDC daemon. > This code is completely Xen specific and depends on the presence of > the Xen management stack, so it must only be compiled in to the > specific Xen device model qemu executable. We would be happy to have > this code in qemu upstream, but it is quite closely linked to much of > the Xen management toolstack and quite loosely coupled to the rest of > qemu so in practice this is not currently a priority for us. >I think that there is room to discuss these sort of things. I think most of these things will actually fall into the first category where we could add the same mechanism to QEMU (perhaps via the monitor), and then Xen could have a small shim that interacted with both XenStore and the QEMU monitor. But really, this isn''t the low hanging fruit so we can cross this bridge when we come to it :-)> There are also another set of concievable things: _emulations_ in qemu > of the Xen environment, so that a Xen PV guest can be run without the > Xen hypervisor and host infrastructure, or so that Xen PV drivers can > be used to improve the performance of a plain qemu guest. > > That includes, for example, xenstore/xenbus emulation, Xen PV backend > drivers, and the like. Gerd''s patches contained several such things. >The patch series that Gerd posted has nothing to do with a Xen shim or emulation layer. The PV backends are directly usable by Xen. Now if ya''ll want nothing to do with these backends, then I''m inclined to wait to add those backends until they are being used by something before pulling it into QEMU. There''s no point in adding code to QEMU that noone is actually going to use. So that may mean waiting to pull in the userspace net/block backends until Xenner can be merged into QEMU. That seems fine to me.> I think a qemu emulator which can support Xen guests without needing > Xen is in principle a fine and good thing to have in qemu upstream. > We don''t have it in our Xen tree because we have the real hypervisor > and dom0-provided backends. But I''m sure we''d find them useful for > debugging. > > BUT we have to not get confused so the names must be different. >So far, I haven''t seen patches on qemu-devel that are for a Xen shim/emulation layer so that doesn''t seem like a problem to me. I assume anything related to the shim/emulation layer will be clearly identified as ''Xenner'' anyway.> It is necessary to clearly distinguish these two use cases: > > * qemu-dm run under the Xen hypervisor, xend, with xenstore, to > support Xen guests as currently done in the Xen upstream project > > * qemu emulating the Xen environment > > I think these should be given different names. We should not use > `xen'' for both. >To me, this isn''t a big issue. Whether we have one or two machine types will be determined by the implementations. What I want to see is as much code sharing as possible for both cases. Regards, Anthony Liguori _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Gerd Hoffmann
2008-Aug-11 19:34 UTC
[Xen-devel] Re: [Qemu-devel] Xen''s qemu branches, etc.
Hi,> Code in qemu intended to support Xen guests without the use of the Xen > hypervisor and tools should be called `xenemu'' or some such.Sure, right now I have the files and functions prefixed with "xenner".> This > includes backend drivers for block and network devices, > xenstore/xenbus emulation, and so on.Yes, for xenstore/xenbus/... emulation. No for the backends. They run on xen just fine too. Additionally you can run xen domains *with* the xen hypervisor but *without* the management stack. cheers, Gerd -- http://kraxel.fedorapeople.org/xenner/ _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel