From patchwork Sun Jul 10 11:47:46 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emil Condrea X-Patchwork-Id: 9222609 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id B753A60572 for ; Sun, 10 Jul 2016 12:08:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id A6EBA24B5B for ; Sun, 10 Jul 2016 12:08:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 9B8A92705B; Sun, 10 Jul 2016 12:08:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 9D6B524B5B for ; Sun, 10 Jul 2016 12:08:11 +0000 (UTC) Received: from localhost ([::1]:54370 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bMDWs-0002ru-Qo for patchwork-qemu-devel@patchwork.kernel.org; Sun, 10 Jul 2016 08:08:10 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:58966) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bMDDz-0006Im-Kl for qemu-devel@nongnu.org; Sun, 10 Jul 2016 07:48:41 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bMDDx-0001mf-P8 for qemu-devel@nongnu.org; Sun, 10 Jul 2016 07:48:39 -0400 Received: from mail-wm0-x241.google.com ([2a00:1450:400c:c09::241]:35990) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bMDDx-0001m8-Ez for qemu-devel@nongnu.org; Sun, 10 Jul 2016 07:48:37 -0400 Received: by mail-wm0-x241.google.com with SMTP id x83so11999538wma.3 for ; Sun, 10 Jul 2016 04:48:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qt2UrpXrBbMUg+DTriwMdd7wNDuEuKztK/yyC81mhy0=; b=srjaQribtTX0zX4AHa4g9wwf93DpZwhVnfaDOdGOpkd4KsQUo3YoCpbH0iMwVbIjhZ MVw7iclYHQg7hQlMB0nSoksaYuU6dQ2JU0AgoGM7LAuJI83ioOe65KjVTZAk0hn4e9nb vj4jJkSQwqslVn2KPKnNB3L5NynbG+uedkaXsM6rBHURbfHe0E1Ylm0e68zov2Ab3xP/ PZj55y/kfvABRDA5/G4phvfq9c7Y7WM/dY8lAhXXeeVK06zGY7P6ngDqqLNHNmS1oI2T LQ2MRZCHxhYt3UPtz2QhrnZcRyN25tVbbTic/RfRBhIzndrFNIyQ0mOM3zrqowEYi2dA W1Lg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qt2UrpXrBbMUg+DTriwMdd7wNDuEuKztK/yyC81mhy0=; b=WaZh2jJrlNnEKQd5iMcHBhMIbUbX/5m88xcmzdtMU/IUZYPGppCteGb+vBacF4grXV Ndi6nHSKsYgi/nH1UOOGVAXUPoslb7WaGIO76OhytucX9+kV0gez36pyK84QVxu/2Qkk do/3i0QPGhxxAS6/jb54c6qrkzsdP1iso/A6/+FaXp1PkwdqVsLmBIX8nVltZtCjuEZY hAJDCZI78pjZK2B+hBtwEMhIB9sU+paqxl6dCu9RQKks4r5bwMNt8gWd+vB7JDfGY4uZ oO4Yo+uNePnSqj7GmgNeF9ftWMTFhYr961HNMxg8mTApjpZSm03Tvp4YibQWFEfUJaR8 AJXw== X-Gm-Message-State: ALyK8tIFyIJbO99q0jb9ID5/YjcRJooSKet7pQRx44lOK5Q3TMw2dRC+mcqFLk5SBV+vVA== X-Received: by 10.28.142.194 with SMTP id q185mr7124763wmd.6.1468151316716; Sun, 10 Jul 2016 04:48:36 -0700 (PDT) Received: from localhost.localdomain ([79.112.8.37]) by smtp.gmail.com with ESMTPSA id d137sm6888378wmd.12.2016.07.10.04.48.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sun, 10 Jul 2016 04:48:35 -0700 (PDT) From: Emil Condrea To: qemu-devel@nongnu.org Date: Sun, 10 Jul 2016 14:47:46 +0300 Message-Id: <1468151270-12984-16-git-send-email-emilcondrea@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1468151270-12984-1-git-send-email-emilcondrea@gmail.com> References: <1468151270-12984-1-git-send-email-emilcondrea@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::241 Subject: [Qemu-devel] [PATCH 15/19] Qemu-Xen-vTPM: Xen frontend driver infrastructure X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: wei.liu2@citrix.com, stefanb@linux.vnet.ibm.com, stefano.stabellini@eu.citrix.com, xen-devel@lists.xen.org, quan.xu@intel.com, dgdegra@tycho.nsa.gov, emilcondrea@gmail.com Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP This patch adds infrastructure for xen front 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 XenStore, setting up XenStore watches, callbacks on device discovery and state changes, and handle event channel between the virtual machines. Call xen_fe_register() function to register XenDevOps, and make sure, XenDevOps's flags is DEVOPS_FLAG_FE, which is flag bit to point out the XenDevOps is Xen frontend. Signed-off-by: Quan Xu Signed-off-by: Emil Condrea --- Changes in v9: * xenstore_dev should not be global, it will not work correctly with multiple xen frontends living in qemu * reuse some common functions: - xen_fe_printf -> xen_pv_printf - xen_fe_evtchn_event -> xen_pv_evtchn_event * use libxenevtchn stable API instead of xc_* calls: - xc_evtchn_fd -> xenevtchn_fd - xc_evtchn_close -> xenevtchn_close - xc_evtchn_bind_unbound_port -> xenevtchn_bind_unbound_port --- hw/xen/xen_frontend.c | 308 ++++++++++++++++++++++++++++++++++++++++++ hw/xen/xen_pvdev.c | 15 ++ include/hw/xen/xen_frontend.h | 6 + include/hw/xen/xen_pvdev.h | 1 + 4 files changed, 330 insertions(+) diff --git a/hw/xen/xen_frontend.c b/hw/xen/xen_frontend.c index 6b92cf1..7b305ce 100644 --- a/hw/xen/xen_frontend.c +++ b/hw/xen/xen_frontend.c @@ -3,6 +3,10 @@ * * (c) 2008 Gerd Hoffmann * + * Copyright (c) 2015 Intel Corporation + * Authors: + * Quan Xu + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -23,6 +27,8 @@ #include "hw/xen/xen_frontend.h" #include "hw/xen/xen_backend.h" +static int debug = 0; + char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) { return xenstore_read_str(xendev->fe, node); @@ -86,3 +92,305 @@ void xenstore_update_fe(char *watch, struct XenDevice *xendev) xen_fe_frontend_changed(xendev, node); xen_be_check_state(xendev); } + +struct XenDevice *xen_fe_get_xendev(const char *type, int dom, int dev, + char *backend, struct XenDevOps *ops) +{ + struct XenDevice *xendev; + + xendev = xen_pv_find_xendev(type, dom, dev); + if (xendev) { + return xendev; + } + + /* init new xendev */ + xendev = g_malloc0(ops->size); + xendev->type = type; + xendev->dom = dom; + xendev->dev = dev; + xendev->ops = ops; + + /*return if the ops->flags is not DEVOPS_FLAG_FE*/ + if (!(ops->flags & DEVOPS_FLAG_FE)) { + return NULL; + } + + snprintf(xendev->be, sizeof(xendev->be), "%s", backend); + snprintf(xendev->name, sizeof(xendev->name), "%s-%d", + xendev->type, xendev->dev); + + xendev->debug = debug; + xendev->local_port = -1; + + xendev->evtchndev = xenevtchn_open(NULL, 0); + if (xendev->evtchndev == NULL) { + xen_pv_printf(NULL, 0, "can't open evtchn device\n"); + g_free(xendev); + return NULL; + } + fcntl(xenevtchn_fd(xendev->evtchndev), F_SETFD, FD_CLOEXEC); + + if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); + xenevtchn_close(xendev->evtchndev); + g_free(xendev); + return NULL; + } + } else { + xendev->gnttabdev = NULL; + } + + xen_pv_insert_xendev(xendev); + + if (xendev->ops->alloc) { + xendev->ops->alloc(xendev); + } + + return xendev; +} + +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom){ + xendev->local_port = xenevtchn_bind_unbound_port(xendev->evtchndev, + remote_dom); + if (xendev->local_port == -1) { + xen_pv_printf(xendev, 0, "xenevtchn_bind_unbound_port failed\n"); + return -1; + } + xen_pv_printf(xendev, 2, "bind evtchn port %d\n", xendev->local_port); + qemu_set_fd_handler(xenevtchn_fd(xendev->evtchndev), + xen_pv_evtchn_event, NULL, xendev); + return 0; +} + +/* + * Make sure, initialize the 'xendev->fe' in xendev->ops->init() or + * xendev->ops->initialize() + */ +int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus) +{ + xs_transaction_t xbt = XBT_NULL; + + if (xendev->fe_state == xbus) { + return 0; + } + + xendev->fe_state = xbus; + if (xendev->fe == NULL) { + xen_pv_printf(NULL, 0, "xendev->fe is NULL\n"); + return -1; + } + +retry_transaction: + xbt = xs_transaction_start(xenstore); + if (xbt == XBT_NULL) { + goto abort_transaction; + } + + if (xenstore_write_int(xendev->fe, "state", xbus)) { + goto abort_transaction; + } + + if (!xs_transaction_end(xenstore, xbt, 0)) { + if (errno == EAGAIN) { + goto retry_transaction; + } + } + + return 0; + +abort_transaction: + xs_transaction_end(xenstore, xbt, 1); + return -1; +} + +/* + * Simplify QEMU side, a thread is running in Xen backend, which will + * connect frontend when the frontend is initialised. Call these initialised + * functions. + */ +static int xen_fe_try_init(void *opaque) +{ + struct XenDevOps *ops = opaque; + int rc = -1; + + if (ops->init) { + rc = ops->init(NULL); + } + + return rc; +} + +static int xen_fe_try_initialise(struct XenDevice *xendev) +{ + int rc = 0, fe_state; + + if (xenstore_read_fe_int(xendev, "state", &fe_state) == -1) { + fe_state = XenbusStateUnknown; + } + xendev->fe_state = fe_state; + + if (xendev->ops->initialise) { + rc = xendev->ops->initialise(xendev); + } + if (rc != 0) { + xen_pv_printf(xendev, 0, "initialise() failed\n"); + return rc; + } + + xenbus_switch_state(xendev, XenbusStateInitialised); + return 0; +} + +static void xen_fe_try_connected(struct XenDevice *xendev) +{ + if (!xendev->ops->connected) { + return; + } + + if (xendev->fe_state != XenbusStateConnected) { + if (xendev->ops->flags & DEVOPS_FLAG_IGNORE_STATE) { + xen_pv_printf(xendev, 2, "frontend not ready, ignoring\n"); + } else { + xen_pv_printf(xendev, 2, "frontend not ready (yet)\n"); + return; + } + } + + xendev->ops->connected(xendev); +} + +static int xen_fe_check(struct XenDevice *xendev, uint32_t domid, + int handle) +{ + int rc = 0; + + rc = xen_fe_try_initialise(xendev); + if (rc != 0) { + xen_pv_printf(xendev, 0, "xendev %s initialise error\n", + xendev->name); + goto err; + } + xen_fe_try_connected(xendev); + + return rc; + +err: + xen_pv_del_xendev(domid, handle); + return -1; +} + +static char *xenstore_fe_get_backend(const char *type, int be_domid, + uint32_t domid, int *hdl) +{ + char *name, *str, *ret = NULL; + uint32_t i, cdev; + int handle = 0; + char path[XEN_BUFSIZE]; + char **dev = NULL; + + name = xenstore_get_domain_name(domid); + snprintf(path, sizeof(path), "frontend/%s/%d", type, be_domid); + dev = xs_directory(xenstore, 0, path, &cdev); + for (i = 0; i < cdev; i++) { + handle = i; + snprintf(path, sizeof(path), "frontend/%s/%d/%d", + type, be_domid, handle); + str = xenstore_read_str(path, "domain"); + if (!strcmp(name, str)) { + break; + } + + free(str); + + /* Not the backend domain */ + if (handle == (cdev - 1)) { + goto err; + } + } + + snprintf(path, sizeof(path), "frontend/%s/%d/%d", + type, be_domid, handle); + str = xenstore_read_str(path, "backend"); + if (str != NULL) { + ret = g_strdup(str); + free(str); + } + + *hdl = handle; + free(dev); + + return ret; +err: + *hdl = -1; + free(dev); + return NULL; +} + +static int xenstore_fe_scan(const char *type, uint32_t domid, + struct XenDevOps *ops) +{ + struct XenDevice *xendev; + char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; + unsigned int cdev, j; + char *backend; + char **dev = NULL; + int rc; + int xenstore_dev; + + /* ops .init check, xendev is NOT initialized */ + rc = xen_fe_try_init(ops); + if (rc != 0) { + return -1; + } + + /* Get /local/domain/0/${type}/{} directory */ + snprintf(path, sizeof(path), "frontend/%s", type); + dev = xs_directory(xenstore, 0, path, &cdev); + if (dev == NULL) { + return 0; + } + + for (j = 0; j < cdev; j++) { + + /* Get backend via domain name */ + backend = xenstore_fe_get_backend(type, atoi(dev[j]), + domid, &xenstore_dev); + if (backend == NULL) { + continue; + } + + xendev = xen_fe_get_xendev(type, domid, xenstore_dev, backend, ops); + free(backend); + if (xendev == NULL) { + xen_pv_printf(xendev, 0, "xendev is NULL.\n"); + continue; + } + + /* + * Simplify QEMU side, a thread is running in Xen backend, which will + * connect frontend when the frontend is initialised. + */ + if (xen_fe_check(xendev, domid, xenstore_dev) < 0) { + xen_pv_printf(xendev, 0, "xendev fe_check error.\n"); + continue; + } + + /* Setup watch */ + snprintf(token, sizeof(token), "be:%p:%d:%p", + type, domid, xendev->ops); + if (!xs_watch(xenstore, xendev->be, token)) { + xen_pv_printf(xendev, 0, "xs_watch failed.\n"); + continue; + } + } + + free(dev); + return 0; +} + +int xen_fe_register(const char *type, struct XenDevOps *ops) +{ + return xenstore_fe_scan(type, xen_domid, ops); +} diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index 394ddc1..cb51e87 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -121,6 +121,21 @@ cleanup: free(vec); } +char *xenstore_get_domain_name(uint32_t domid) +{ + char *dom_path, *str, *ret = NULL; + + dom_path = xs_get_domain_path(xenstore, domid); + str = xenstore_read_str(dom_path, "name"); + free(dom_path); + if (str != NULL) { + ret = g_strdup(str); + free(str); + } + + return ret; +} + const char *xenbus_strstate(enum xenbus_state state) { static const char *const name[] = { diff --git a/include/hw/xen/xen_frontend.h b/include/hw/xen/xen_frontend.h index 5d03f4e..8fc0422 100644 --- a/include/hw/xen/xen_frontend.h +++ b/include/hw/xen/xen_frontend.h @@ -8,6 +8,12 @@ int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, uint64_t *uval); void xenstore_update_fe(char *watch, struct XenDevice *xendev); +struct XenDevice *xen_fe_get_xendev(const char *type, int dom, int dev, + char *backend, struct XenDevOps *ops); void xen_fe_frontend_changed(struct XenDevice *xendev, const char *node); +int xen_fe_register(const char *type, struct XenDevOps *ops); +int xen_fe_alloc_unbound(struct XenDevice *xendev, int dom, int remote_dom); +int xenbus_switch_state(struct XenDevice *xendev, enum xenbus_state xbus); + #endif /* QEMU_HW_XEN_FRONTEND_H */ diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index c985a9d..11bab56 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -65,6 +65,7 @@ char *xenstore_read_str(const char *base, const char *node); int xenstore_read_int(const char *base, const char *node, int *ival); int xenstore_read_uint64(const char *base, const char *node, uint64_t *uval); void xenstore_update(void *unused); +char *xenstore_get_domain_name(uint32_t domid); const char *xenbus_strstate(enum xenbus_state state);