linux/virt/kvm/async_pf.c
<<
>>
Prefs
   1/*
   2 * kvm asynchronous fault support
   3 *
   4 * Copyright 2010 Red Hat, Inc.
   5 *
   6 * Author:
   7 *      Gleb Natapov <gleb@redhat.com>
   8 *
   9 * This file is free software; you can redistribute it and/or modify
  10 * it under the terms of version 2 of the GNU General Public License
  11 * as published by the Free Software Foundation.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software Foundation,
  20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  21 */
  22
  23#include <linux/kvm_host.h>
  24#include <linux/slab.h>
  25#include <linux/module.h>
  26#include <linux/mmu_context.h>
  27
  28#include "async_pf.h"
  29#include <trace/events/kvm.h>
  30
  31static inline void kvm_async_page_present_sync(struct kvm_vcpu *vcpu,
  32                                               struct kvm_async_pf *work)
  33{
  34#ifdef CONFIG_KVM_ASYNC_PF_SYNC
  35        kvm_arch_async_page_present(vcpu, work);
  36#endif
  37}
  38static inline void kvm_async_page_present_async(struct kvm_vcpu *vcpu,
  39                                                struct kvm_async_pf *work)
  40{
  41#ifndef CONFIG_KVM_ASYNC_PF_SYNC
  42        kvm_arch_async_page_present(vcpu, work);
  43#endif
  44}
  45
  46static struct kmem_cache *async_pf_cache;
  47
  48int kvm_async_pf_init(void)
  49{
  50        async_pf_cache = KMEM_CACHE(kvm_async_pf, 0);
  51
  52        if (!async_pf_cache)
  53                return -ENOMEM;
  54
  55        return 0;
  56}
  57
  58void kvm_async_pf_deinit(void)
  59{
  60        kmem_cache_destroy(async_pf_cache);
  61        async_pf_cache = NULL;
  62}
  63
  64void kvm_async_pf_vcpu_init(struct kvm_vcpu *vcpu)
  65{
  66        INIT_LIST_HEAD(&vcpu->async_pf.done);
  67        INIT_LIST_HEAD(&vcpu->async_pf.queue);
  68        spin_lock_init(&vcpu->async_pf.lock);
  69}
  70
  71static void async_pf_execute(struct work_struct *work)
  72{
  73        struct kvm_async_pf *apf =
  74                container_of(work, struct kvm_async_pf, work);
  75        struct mm_struct *mm = apf->mm;
  76        struct kvm_vcpu *vcpu = apf->vcpu;
  77        unsigned long addr = apf->addr;
  78        gva_t gva = apf->gva;
  79
  80        might_sleep();
  81
  82        /*
  83         * This work is run asynchromously to the task which owns
  84         * mm and might be done in another context, so we must
  85         * use FOLL_REMOTE.
  86         */
  87        __get_user_pages_unlocked(NULL, mm, addr, 1, NULL,
  88                        FOLL_WRITE | FOLL_REMOTE);
  89
  90        kvm_async_page_present_sync(vcpu, apf);
  91
  92        spin_lock(&vcpu->async_pf.lock);
  93        list_add_tail(&apf->link, &vcpu->async_pf.done);
  94        apf->vcpu = NULL;
  95        spin_unlock(&vcpu->async_pf.lock);
  96
  97        /*
  98         * apf may be freed by kvm_check_async_pf_completion() after
  99         * this point
 100         */
 101
 102        trace_kvm_async_pf_completed(addr, gva);
 103
 104        /*
 105         * This memory barrier pairs with prepare_to_wait's set_current_state()
 106         */
 107        smp_mb();
 108        if (swait_active(&vcpu->wq))
 109                swake_up(&vcpu->wq);
 110
 111        mmput(mm);
 112        kvm_put_kvm(vcpu->kvm);
 113}
 114
 115void kvm_clear_async_pf_completion_queue(struct kvm_vcpu *vcpu)
 116{
 117        spin_lock(&vcpu->async_pf.lock);
 118
 119        /* cancel outstanding work queue item */
 120        while (!list_empty(&vcpu->async_pf.queue)) {
 121                struct kvm_async_pf *work =
 122                        list_first_entry(&vcpu->async_pf.queue,
 123                                         typeof(*work), queue);
 124                list_del(&work->queue);
 125
 126                /*
 127                 * We know it's present in vcpu->async_pf.done, do
 128                 * nothing here.
 129                 */
 130                if (!work->vcpu)
 131                        continue;
 132
 133                spin_unlock(&vcpu->async_pf.lock);
 134#ifdef CONFIG_KVM_ASYNC_PF_SYNC
 135                flush_work(&work->work);
 136#else
 137                if (cancel_work_sync(&work->work)) {
 138                        mmput(work->mm);
 139                        kvm_put_kvm(vcpu->kvm); /* == work->vcpu->kvm */
 140                        kmem_cache_free(async_pf_cache, work);
 141                }
 142#endif
 143                spin_lock(&vcpu->async_pf.lock);
 144        }
 145
 146        while (!list_empty(&vcpu->async_pf.done)) {
 147                struct kvm_async_pf *work =
 148                        list_first_entry(&vcpu->async_pf.done,
 149                                         typeof(*work), link);
 150                list_del(&work->link);
 151                kmem_cache_free(async_pf_cache, work);
 152        }
 153        spin_unlock(&vcpu->async_pf.lock);
 154
 155        vcpu->async_pf.queued = 0;
 156}
 157
 158void kvm_check_async_pf_completion(struct kvm_vcpu *vcpu)
 159{
 160        struct kvm_async_pf *work;
 161
 162        while (!list_empty_careful(&vcpu->async_pf.done) &&
 163              kvm_arch_can_inject_async_page_present(vcpu)) {
 164                spin_lock(&vcpu->async_pf.lock);
 165                work = list_first_entry(&vcpu->async_pf.done, typeof(*work),
 166                                              link);
 167                list_del(&work->link);
 168                spin_unlock(&vcpu->async_pf.lock);
 169
 170                kvm_arch_async_page_ready(vcpu, work);
 171                kvm_async_page_present_async(vcpu, work);
 172
 173                list_del(&work->queue);
 174                vcpu->async_pf.queued--;
 175                kmem_cache_free(async_pf_cache, work);
 176        }
 177}
 178
 179int kvm_setup_async_pf(struct kvm_vcpu *vcpu, gva_t gva, unsigned long hva,
 180                       struct kvm_arch_async_pf *arch)
 181{
 182        struct kvm_async_pf *work;
 183
 184        if (vcpu->async_pf.queued >= ASYNC_PF_PER_VCPU)
 185                return 0;
 186
 187        /* setup delayed work */
 188
 189        /*
 190         * do alloc nowait since if we are going to sleep anyway we
 191         * may as well sleep faulting in page
 192         */
 193        work = kmem_cache_zalloc(async_pf_cache, GFP_NOWAIT | __GFP_NOWARN);
 194        if (!work)
 195                return 0;
 196
 197        work->wakeup_all = false;
 198        work->vcpu = vcpu;
 199        work->gva = gva;
 200        work->addr = hva;
 201        work->arch = *arch;
 202        work->mm = current->mm;
 203        atomic_inc(&work->mm->mm_users);
 204        kvm_get_kvm(work->vcpu->kvm);
 205
 206        /* this can't really happen otherwise gfn_to_pfn_async
 207           would succeed */
 208        if (unlikely(kvm_is_error_hva(work->addr)))
 209                goto retry_sync;
 210
 211        INIT_WORK(&work->work, async_pf_execute);
 212        if (!schedule_work(&work->work))
 213                goto retry_sync;
 214
 215        list_add_tail(&work->queue, &vcpu->async_pf.queue);
 216        vcpu->async_pf.queued++;
 217        kvm_arch_async_page_not_present(vcpu, work);
 218        return 1;
 219retry_sync:
 220        kvm_put_kvm(work->vcpu->kvm);
 221        mmput(work->mm);
 222        kmem_cache_free(async_pf_cache, work);
 223        return 0;
 224}
 225
 226int kvm_async_pf_wakeup_all(struct kvm_vcpu *vcpu)
 227{
 228        struct kvm_async_pf *work;
 229
 230        if (!list_empty_careful(&vcpu->async_pf.done))
 231                return 0;
 232
 233        work = kmem_cache_zalloc(async_pf_cache, GFP_ATOMIC);
 234        if (!work)
 235                return -ENOMEM;
 236
 237        work->wakeup_all = true;
 238        INIT_LIST_HEAD(&work->queue); /* for list_del to work */
 239
 240        spin_lock(&vcpu->async_pf.lock);
 241        list_add_tail(&work->link, &vcpu->async_pf.done);
 242        spin_unlock(&vcpu->async_pf.lock);
 243
 244        vcpu->async_pf.queued++;
 245        return 0;
 246}
 247