From patchwork Wed Feb 11 08:32:22 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Auger Eric X-Patchwork-Id: 44568 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-wg0-f72.google.com (mail-wg0-f72.google.com [74.125.82.72]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id CD64421527 for ; Wed, 11 Feb 2015 08:35:52 +0000 (UTC) Received: by mail-wg0-f72.google.com with SMTP id l18sf1210190wgh.3 for ; Wed, 11 Feb 2015 00:35:52 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :list-post:list-help:list-archive:list-unsubscribe; bh=DfQVgCcOoiCvk4TYeNhLWyxv7YAzMH15pfk4olPl97M=; b=iXD+rracKgX7Lj88fbDEP6B533KgnnLo8wR1n2yHuFpdQ7Vr5k4Yi0P9hpIIV4QryU 94dxZL9cwtqycH76xjAbx0fvLkrR0MGM9q1f4dLWCvpQxiL8ObTAkqaG4uXpFuDDB5Kv x4kcn8PEQgpH3qyB4coC5sA4AJJ/BJEjWdBhe1tZbbImVg9XWbTZYdG5/TKSLVjuIY8F 7BaSdbsZxqaYSHYbkirohUZY1j20e8UpRhufESObv96V9uLUtN/P1ScQ+mJmIb721jws NErv0ByDGx1i2UsGCets16Kx56lToIvUW7pQ7mfdtN2E27uPrT52tVsQMYLNJFpUIKru Y+DQ== X-Gm-Message-State: ALoCoQlN+Fq282G1R9BAosF2jwUKc63E5geEpU2PzC1yKkqIb6avR86ybZA3inSGek4m8wa2iFyT X-Received: by 10.112.168.104 with SMTP id zv8mr3254038lbb.10.1423643752087; Wed, 11 Feb 2015 00:35:52 -0800 (PST) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.7.100 with SMTP id i4ls25326laa.106.gmail; Wed, 11 Feb 2015 00:35:51 -0800 (PST) X-Received: by 10.112.64.2 with SMTP id k2mr7224236lbs.54.1423643751762; Wed, 11 Feb 2015 00:35:51 -0800 (PST) Received: from mail-la0-f42.google.com (mail-la0-f42.google.com. [209.85.215.42]) by mx.google.com with ESMTPS id rb6si118869lbb.8.2015.02.11.00.35.51 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 11 Feb 2015 00:35:51 -0800 (PST) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.42 as permitted sender) client-ip=209.85.215.42; Received: by labhv19 with SMTP id hv19so1825824lab.10 for ; Wed, 11 Feb 2015 00:35:51 -0800 (PST) X-Received: by 10.152.181.168 with SMTP id dx8mr26261708lac.76.1423643751671; Wed, 11 Feb 2015 00:35:51 -0800 (PST) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.112.35.133 with SMTP id h5csp1110444lbj; Wed, 11 Feb 2015 00:35:50 -0800 (PST) X-Received: by 10.42.102.5 with SMTP id g5mr1838695ico.74.1423643749795; Wed, 11 Feb 2015 00:35:49 -0800 (PST) Received: from mail-ie0-f177.google.com (mail-ie0-f177.google.com. [209.85.223.177]) by mx.google.com with ESMTPS id dz15si1232473icb.71.2015.02.11.00.35.49 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 11 Feb 2015 00:35:49 -0800 (PST) Received-SPF: pass (google.com: domain of eric.auger@linaro.org designates 209.85.223.177 as permitted sender) client-ip=209.85.223.177; Received: by iecat20 with SMTP id at20so2301703iec.12 for ; Wed, 11 Feb 2015 00:35:49 -0800 (PST) X-Received: by 10.51.17.40 with SMTP id gb8mr2254710igd.44.1423643749021; Wed, 11 Feb 2015 00:35:49 -0800 (PST) Received: from gnx2579.solutionip.com ([113.28.134.59]) by mx.google.com with ESMTPSA id m38sm62189ioi.39.2015.02.11.00.35.44 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Wed, 11 Feb 2015 00:35:47 -0800 (PST) From: Eric Auger To: eric.auger@st.com, eric.auger@linaro.org, christoffer.dall@linaro.org, marc.zyngier@arm.com, linux-arm-kernel@lists.infradead.org, kvmarm@lists.cs.columbia.edu, kvm@vger.kernel.org, alex.williamson@redhat.com, feng.wu@intel.com Cc: andre.przywara@arm.com, patches@linaro.org, a.motakis@virtualopensystems.com, a.rigo@virtualopensystems.com, b.reynal@virtualopensystems.com, gleb@kernel.org, pbonzini@redhat.com Subject: [RFC v4 12/13] KVM: kvm-vfio: generic forwarding control Date: Wed, 11 Feb 2015 09:32:22 +0100 Message-Id: <1423643543-24409-13-git-send-email-eric.auger@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1423643543-24409-1-git-send-email-eric.auger@linaro.org> References: <1423643543-24409-1-git-send-email-eric.auger@linaro.org> X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: eric.auger@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.42 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch introduces a new KVM_DEV_VFIO_DEVICE group. This is a new control channel which enables KVM to cooperate with viable VFIO devices. The patch introduces 2 attributes for this group: KVM_DEV_VFIO_DEVICE_FORWARD_IRQ, KVM_DEV_VFIO_DEVICE_UNFORWARD_IRQ. Their purpose is to turn a VFIO device IRQ into a forwarded IRQ and respectively unset the feature. The generic part introduced here interact with VFIO, genirq, KVM while the architecture specific part mostly takes care of the virtual interrupt controller programming. Architecture specific implementation is enabled when __KVM_HAVE_ARCH_KVM_VFIO_FORWARD is set. When not set those functions are void. Signed-off-by: Eric Auger --- v3 -> v4: - use new kvm_vfio_dev_irq struct - improve error handling according to Alex comments - full rework or generic/arch specific split to accomodate for unforward that never fails - kvm_vfio_get_vfio_device and kvm_vfio_put_vfio_device removed from that patch file and introduced before (since also used by Feng) - guard kvm_vfio_control_irq_forward call with __KVM_HAVE_ARCH_KVM_VFIO_FORWARD v2 -> v3: - add API comments in kvm_host.h - improve the commit message - create a private kvm_vfio_fwd_irq struct - fwd_irq_action replaced by a bool and removal of VFIO_IRQ_CLEANUP. This latter action will be handled in vgic. - add a vfio_device handle argument to kvm_arch_set_fwd_state. The goal is to move platform specific stuff in architecture specific code. - kvm_arch_set_fwd_state renamed into kvm_arch_vfio_set_forward - increment the ref counter each time we do an IRQ forwarding and decrement this latter each time one IRQ forward is unset. Simplifies the whole ref counting. - simplification of list handling: create, search, removal v1 -> v2: - __KVM_HAVE_ARCH_KVM_VFIO renamed into __KVM_HAVE_ARCH_KVM_VFIO_FORWARD - original patch file separated into 2 parts: generic part moved in vfio.c and ARM specific part(kvm_arch_set_fwd_state) --- include/linux/kvm_host.h | 47 +++++++ virt/kvm/vfio.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 355 insertions(+), 3 deletions(-) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 81c93de..f2bc192 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1048,6 +1048,15 @@ struct kvm_device_ops { unsigned long arg); }; +/* internal self-contained structure describing a forwarded IRQ */ +struct kvm_fwd_irq { + struct kvm *kvm; /* VM to inject the GSI into */ + struct vfio_device *vdev; /* vfio device the IRQ belongs to */ + __u32 index; /* VFIO device IRQ index */ + __u32 subindex; /* VFIO device IRQ subindex */ + __u32 gsi; /* gsi, ie. virtual IRQ number */ +}; + void kvm_device_get(struct kvm_device *dev); void kvm_device_put(struct kvm_device *dev); struct kvm_device *kvm_device_from_filp(struct file *filp); @@ -1069,6 +1078,44 @@ inline void kvm_arch_resume_guest(struct kvm *kvm) {} #endif +#ifdef __KVM_HAVE_ARCH_KVM_VFIO_FORWARD +/** + * kvm_arch_set_forward - Sets forwarding for a given IRQ + * + * @kvm: handle to the VM + * @host_irq: physical IRQ number + * @guest_irq: virtual IRQ number + * returns 0 on success, < 0 on failure + */ +int kvm_arch_set_forward(struct kvm *kvm, + unsigned int host_irq, unsigned int guest_irq); + +/** + * kvm_arch_unset_forward - Unsets forwarding for a given IRQ + * + * @kvm: handle to the VM + * @host_irq: physical IRQ number + * @guest_irq: virtual IRQ number + * @active: returns whether the IRQ is active + */ +void kvm_arch_unset_forward(struct kvm *kvm, + unsigned int host_irq, + unsigned int guest_irq, + bool *active); + +#else +static inline int kvm_arch_set_forward(struct kvm *kvm, + unsigned int host_irq, + unsigned int guest_irq) +{ + return -ENOENT; +} +static inline void kvm_arch_unset_forward(struct kvm *kvm, + unsigned int host_irq, + unsigned int guest_irq, + bool *active) {} +#endif + #ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT static inline void kvm_vcpu_set_in_spin_loop(struct kvm_vcpu *vcpu, bool val) diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c index c995e51..4847597 100644 --- a/virt/kvm/vfio.c +++ b/virt/kvm/vfio.c @@ -19,14 +19,30 @@ #include #include #include "vfio.h" +#include +#include +#include +#include +#include + +#define DEBUG_FORWARD +#define DEBUG_UNFORWARD struct kvm_vfio_group { struct list_head node; struct vfio_group *vfio_group; }; +/* private linkable kvm_fwd_irq struct */ +struct kvm_vfio_fwd_irq_node { + struct list_head link; + struct kvm_fwd_irq fwd_irq; +}; + struct kvm_vfio { struct list_head group_list; + /* list of registered VFIO forwarded IRQs */ + struct list_head fwd_node_list; struct mutex lock; bool noncoherent; }; @@ -320,12 +336,293 @@ static int kvm_vfio_set_group(struct kvm_device *dev, long attr, u64 arg) return -ENXIO; } +/** + * kvm_vfio_find_fwd_irq - checks whether a forwarded IRQ already is + * registered in the list of forwarded IRQs + * + * @kv: handle to the kvm-vfio device + * @fwd: handle to the forwarded irq struct + * In the positive returns the handle to its node in the kvm-vfio + * forwarded IRQ list, returns NULL otherwise. + * Must be called with kv->lock hold. + */ +static struct kvm_vfio_fwd_irq_node *kvm_vfio_find_fwd_irq( + struct kvm_vfio *kv, + struct kvm_fwd_irq *fwd) +{ + struct kvm_vfio_fwd_irq_node *node; + + list_for_each_entry(node, &kv->fwd_node_list, link) { + if ((node->fwd_irq.index == fwd->index) && + (node->fwd_irq.subindex == fwd->subindex) && + (node->fwd_irq.vdev == fwd->vdev)) + return node; + } + return NULL; +} +/** + * kvm_vfio_register_fwd_irq - Allocates, populates and registers a + * forwarded IRQ + * + * @kv: handle to the kvm-vfio device + * @fwd: handle to the forwarded irq struct + * In case of success returns a handle to the new list node, + * NULL otherwise. + * Must be called with kv->lock hold. + */ +static struct kvm_vfio_fwd_irq_node *kvm_vfio_register_fwd_irq( + struct kvm_vfio *kv, + struct kvm_fwd_irq *fwd) +{ + struct kvm_vfio_fwd_irq_node *node; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return ERR_PTR(-ENOMEM); + + node->fwd_irq = *fwd; + + list_add(&node->link, &kv->fwd_node_list); + + return node; +} + +/** + * kvm_vfio_unregister_fwd_irq - unregisters and frees a forwarded IRQ + * + * @node: handle to the node struct + * Must be called with kv->lock hold. + */ +static void kvm_vfio_unregister_fwd_irq(struct kvm_vfio_fwd_irq_node *node) +{ + list_del(&node->link); + kvm_vfio_put_vfio_device(node->fwd_irq.vdev); + kfree(node); +} + +static int kvm_vfio_platform_get_irq(struct vfio_device *vdev, int index) +{ + int hwirq; + struct platform_device *platdev; + struct device *dev = kvm_vfio_external_base_device(vdev); + + if (dev->bus == &platform_bus_type) { + platdev = to_platform_device(dev); + hwirq = platform_get_irq(platdev, index); + return hwirq; + } else + return -EINVAL; +} + +/** + * kvm_vfio_set_forward - turns a VFIO device IRQ into a forwarded IRQ + * @kv: handle to the kvm-vfio device + * @fd: file descriptor of the vfio device the IRQ belongs to + * @fwd: handle to the forwarded irq struct + * + * Registers and turns an IRQ as forwarded. The operation only is allowed + * if the IRQ is not in progress (active at interrupt controller level or + * auto-masked by the handler). In case the user-space masked the IRQ, + * the operation will fail too. + * returns: + * -EAGAIN if the IRQ is in progress or VFIO masked + * -EEXIST if the IRQ is already registered as forwarded + * -ENOMEM on memory shortage + */ +static int kvm_vfio_set_forward(struct kvm_vfio *kv, int fd, + struct kvm_fwd_irq *fwd) +{ + int ret = -EAGAIN; + struct kvm_vfio_fwd_irq_node *node; + struct vfio_platform_device *vpdev = vfio_device_data(fwd->vdev); + int index = fwd->index; + int host_irq = kvm_vfio_platform_get_irq(fwd->vdev, fwd->index); + bool active; + + kvm_arch_halt_guest(fwd->kvm); + + disable_irq(host_irq); + + active = kvm_vfio_external_is_active(vpdev, index); + + if (active) + goto out; + + node = kvm_vfio_register_fwd_irq(kv, fwd); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto out; + } + + kvm_vfio_external_set_automasked(vpdev, index, false); + + ret = kvm_arch_set_forward(fwd->kvm, host_irq, fwd->gsi); + +out: + enable_irq(host_irq); + + kvm_arch_resume_guest(fwd->kvm); + + return ret; +} + +/** + * kvm_vfio_unset_forward - Sets a VFIO device IRQ as non forwarded + * @kv: handle to the kvm-vfio device + * @node: handle to the node to unset + * + * Calls the architecture specific implementation of set_forward and + * unregisters the IRQ from the forwarded IRQ list. + */ +static void kvm_vfio_unset_forward(struct kvm_vfio *kv, + struct kvm_vfio_fwd_irq_node *node) +{ + struct kvm_fwd_irq fwd = node->fwd_irq; + int host_irq = kvm_vfio_platform_get_irq(fwd.vdev, fwd.index); + int index = fwd.index; + struct vfio_platform_device *vpdev = vfio_device_data(fwd.vdev); + bool active = false; + + kvm_arch_halt_guest(fwd.kvm); + + disable_irq(host_irq); + + kvm_arch_unset_forward(fwd.kvm, host_irq, fwd.gsi, &active); + + if (active) + kvm_vfio_external_mask(vpdev, index); + + kvm_vfio_external_set_automasked(vpdev, index, true); + enable_irq(host_irq); + + kvm_arch_resume_guest(fwd.kvm); + + kvm_vfio_unregister_fwd_irq(node); +} + +static int kvm_vfio_control_irq_forward(struct kvm_device *kdev, long attr, + int32_t __user *argp) +{ + struct kvm_vfio_dev_irq user_fwd_irq; + struct kvm_fwd_irq fwd; + struct vfio_device *vdev; + struct kvm_vfio *kv = kdev->private; + struct kvm_vfio_fwd_irq_node *node; + unsigned long minsz; + int ret = 0; + u32 *gsi = NULL; + size_t size; + + minsz = offsetofend(struct kvm_vfio_dev_irq, count); + + if (copy_from_user(&user_fwd_irq, argp, minsz)) + return -EFAULT; + + if (user_fwd_irq.argsz < minsz) + return -EINVAL; + + vdev = kvm_vfio_get_vfio_device(user_fwd_irq.fd); + if (IS_ERR(vdev)) + return PTR_ERR(vdev); + + ret = kvm_vfio_platform_get_irq(vdev, user_fwd_irq.index); + if (ret < 0) + goto error; + + size = sizeof(__u32); + if (user_fwd_irq.argsz - minsz < size) { + ret = -EINVAL; + goto error; + } + + gsi = memdup_user((void __user *)((unsigned long)argp + minsz), size); + if (IS_ERR(gsi)) { + ret = PTR_ERR(gsi); + goto error; + } + + fwd.vdev = vdev; + fwd.kvm = kdev->kvm; + fwd.index = user_fwd_irq.index; + fwd.subindex = 0; + fwd.gsi = *gsi; + + node = kvm_vfio_find_fwd_irq(kv, &fwd); + + switch (attr) { + case KVM_DEV_VFIO_DEVICE_FORWARD_IRQ: + if (node) { + ret = -EEXIST; + goto error; + } + mutex_lock(&kv->lock); + ret = kvm_vfio_set_forward(kv, user_fwd_irq.fd, &fwd); + mutex_unlock(&kv->lock); + break; + case KVM_DEV_VFIO_DEVICE_UNFORWARD_IRQ: + if (!node) { + ret = -ENOENT; + goto error; + } + mutex_lock(&kv->lock); + kvm_vfio_unset_forward(kv, node); + mutex_unlock(&kv->lock); + kvm_vfio_put_vfio_device(vdev); + ret = 0; + break; + } + + kfree(gsi); +error: + if (ret < 0) + kvm_vfio_put_vfio_device(vdev); + return ret; +} + +static int kvm_vfio_set_device(struct kvm_device *kdev, long attr, u64 arg) +{ + int32_t __user *argp = (int32_t __user *)(unsigned long)arg; + int ret; + + switch (attr) { +#ifdef __KVM_HAVE_ARCH_KVM_VFIO_FORWARD + case KVM_DEV_VFIO_DEVICE_FORWARD_IRQ: + case KVM_DEV_VFIO_DEVICE_UNFORWARD_IRQ: + ret = kvm_vfio_control_irq_forward(kdev, attr, argp); + break; +#endif + default: + ret = -ENXIO; + } + return ret; +} + +/** + * kvm_vfio_clean_fwd_irq - Unset forwarding state of all + * registered forwarded IRQs and free their list nodes. + * @kv: kvm-vfio device + * + * Loop on all registered device/IRQ combos, reset the non forwarded state, + * void the lists and release the reference + */ +static int kvm_vfio_clean_fwd_irq(struct kvm_vfio *kv) +{ + struct kvm_vfio_fwd_irq_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &kv->fwd_node_list, link) { + kvm_vfio_unset_forward(kv, node); + } + return 0; +} + static int kvm_vfio_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { switch (attr->group) { case KVM_DEV_VFIO_GROUP: return kvm_vfio_set_group(dev, attr->attr, attr->addr); + case KVM_DEV_VFIO_DEVICE: + return kvm_vfio_set_device(dev, attr->attr, attr->addr); } return -ENXIO; @@ -341,10 +638,17 @@ static int kvm_vfio_has_attr(struct kvm_device *dev, case KVM_DEV_VFIO_GROUP_DEL: return 0; } - break; +#ifdef __KVM_HAVE_ARCH_KVM_VFIO_FORWARD + case KVM_DEV_VFIO_DEVICE: + switch (attr->attr) { + case KVM_DEV_VFIO_DEVICE_FORWARD_IRQ: + case KVM_DEV_VFIO_DEVICE_UNFORWARD_IRQ: + return 0; + } + break; +#endif } - return -ENXIO; } @@ -358,7 +662,7 @@ static void kvm_vfio_destroy(struct kvm_device *dev) list_del(&kvg->node); kfree(kvg); } - + kvm_vfio_clean_fwd_irq(kv); kvm_vfio_update_coherency(dev); kfree(kv); @@ -390,6 +694,7 @@ static int kvm_vfio_create(struct kvm_device *dev, u32 type) return -ENOMEM; INIT_LIST_HEAD(&kv->group_list); + INIT_LIST_HEAD(&kv->fwd_node_list); mutex_init(&kv->lock); dev->private = kv;