|
@@ -40,9 +40,34 @@
|
|
|
|
|
|
#include "trace_hv.h"
|
|
|
|
|
|
+//#define DEBUG_RESIZE_HPT 1
|
|
|
+
|
|
|
+#ifdef DEBUG_RESIZE_HPT
|
|
|
+#define resize_hpt_debug(resize, ...) \
|
|
|
+ do { \
|
|
|
+ printk(KERN_DEBUG "RESIZE HPT %p: ", resize); \
|
|
|
+ printk(__VA_ARGS__); \
|
|
|
+ } while (0)
|
|
|
+#else
|
|
|
+#define resize_hpt_debug(resize, ...) \
|
|
|
+ do { } while (0)
|
|
|
+#endif
|
|
|
+
|
|
|
static long kvmppc_virtmode_do_h_enter(struct kvm *kvm, unsigned long flags,
|
|
|
long pte_index, unsigned long pteh,
|
|
|
unsigned long ptel, unsigned long *pte_idx_ret);
|
|
|
+
|
|
|
+struct kvm_resize_hpt {
|
|
|
+ /* These fields read-only after init */
|
|
|
+ struct kvm *kvm;
|
|
|
+ struct work_struct work;
|
|
|
+ u32 order;
|
|
|
+
|
|
|
+ /* These fields protected by kvm->lock */
|
|
|
+ int error;
|
|
|
+ bool prepare_done;
|
|
|
+};
|
|
|
+
|
|
|
static void kvmppc_rmap_reset(struct kvm *kvm);
|
|
|
|
|
|
int kvmppc_allocate_hpt(struct kvm_hpt_info *info, u32 order)
|
|
@@ -1179,6 +1204,172 @@ void kvmppc_unpin_guest_page(struct kvm *kvm, void *va, unsigned long gpa,
|
|
|
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * HPT resizing
|
|
|
+ */
|
|
|
+static int resize_hpt_allocate(struct kvm_resize_hpt *resize)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int resize_hpt_rehash(struct kvm_resize_hpt *resize)
|
|
|
+{
|
|
|
+ return -EIO;
|
|
|
+}
|
|
|
+
|
|
|
+static void resize_hpt_pivot(struct kvm_resize_hpt *resize)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+static void resize_hpt_release(struct kvm *kvm, struct kvm_resize_hpt *resize)
|
|
|
+{
|
|
|
+ BUG_ON(kvm->arch.resize_hpt != resize);
|
|
|
+ kvm->arch.resize_hpt = NULL;
|
|
|
+ kfree(resize);
|
|
|
+}
|
|
|
+
|
|
|
+static void resize_hpt_prepare_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct kvm_resize_hpt *resize = container_of(work,
|
|
|
+ struct kvm_resize_hpt,
|
|
|
+ work);
|
|
|
+ struct kvm *kvm = resize->kvm;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ resize_hpt_debug(resize, "resize_hpt_prepare_work(): order = %d\n",
|
|
|
+ resize->order);
|
|
|
+
|
|
|
+ err = resize_hpt_allocate(resize);
|
|
|
+
|
|
|
+ mutex_lock(&kvm->lock);
|
|
|
+
|
|
|
+ resize->error = err;
|
|
|
+ resize->prepare_done = true;
|
|
|
+
|
|
|
+ mutex_unlock(&kvm->lock);
|
|
|
+}
|
|
|
+
|
|
|
+long kvm_vm_ioctl_resize_hpt_prepare(struct kvm *kvm,
|
|
|
+ struct kvm_ppc_resize_hpt *rhpt)
|
|
|
+{
|
|
|
+ unsigned long flags = rhpt->flags;
|
|
|
+ unsigned long shift = rhpt->shift;
|
|
|
+ struct kvm_resize_hpt *resize;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (flags != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (shift && ((shift < 18) || (shift > 46)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mutex_lock(&kvm->lock);
|
|
|
+
|
|
|
+ resize = kvm->arch.resize_hpt;
|
|
|
+
|
|
|
+ if (resize) {
|
|
|
+ if (resize->order == shift) {
|
|
|
+ /* Suitable resize in progress */
|
|
|
+ if (resize->prepare_done) {
|
|
|
+ ret = resize->error;
|
|
|
+ if (ret != 0)
|
|
|
+ resize_hpt_release(kvm, resize);
|
|
|
+ } else {
|
|
|
+ ret = 100; /* estimated time in ms */
|
|
|
+ }
|
|
|
+
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* not suitable, cancel it */
|
|
|
+ resize_hpt_release(kvm, resize);
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = 0;
|
|
|
+ if (!shift)
|
|
|
+ goto out; /* nothing to do */
|
|
|
+
|
|
|
+ /* start new resize */
|
|
|
+
|
|
|
+ resize = kzalloc(sizeof(*resize), GFP_KERNEL);
|
|
|
+ resize->order = shift;
|
|
|
+ resize->kvm = kvm;
|
|
|
+ INIT_WORK(&resize->work, resize_hpt_prepare_work);
|
|
|
+ kvm->arch.resize_hpt = resize;
|
|
|
+
|
|
|
+ schedule_work(&resize->work);
|
|
|
+
|
|
|
+ ret = 100; /* estimated time in ms */
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&kvm->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void resize_hpt_boot_vcpu(void *opaque)
|
|
|
+{
|
|
|
+ /* Nothing to do, just force a KVM exit */
|
|
|
+}
|
|
|
+
|
|
|
+long kvm_vm_ioctl_resize_hpt_commit(struct kvm *kvm,
|
|
|
+ struct kvm_ppc_resize_hpt *rhpt)
|
|
|
+{
|
|
|
+ unsigned long flags = rhpt->flags;
|
|
|
+ unsigned long shift = rhpt->shift;
|
|
|
+ struct kvm_resize_hpt *resize;
|
|
|
+ long ret;
|
|
|
+
|
|
|
+ if (flags != 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (shift && ((shift < 18) || (shift > 46)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mutex_lock(&kvm->lock);
|
|
|
+
|
|
|
+ resize = kvm->arch.resize_hpt;
|
|
|
+
|
|
|
+ /* This shouldn't be possible */
|
|
|
+ ret = -EIO;
|
|
|
+ if (WARN_ON(!kvm->arch.hpte_setup_done))
|
|
|
+ goto out_no_hpt;
|
|
|
+
|
|
|
+ /* Stop VCPUs from running while we mess with the HPT */
|
|
|
+ kvm->arch.hpte_setup_done = 0;
|
|
|
+ smp_mb();
|
|
|
+
|
|
|
+ /* Boot all CPUs out of the guest so they re-read
|
|
|
+ * hpte_setup_done */
|
|
|
+ on_each_cpu(resize_hpt_boot_vcpu, NULL, 1);
|
|
|
+
|
|
|
+ ret = -ENXIO;
|
|
|
+ if (!resize || (resize->order != shift))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = -EBUSY;
|
|
|
+ if (!resize->prepare_done)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = resize->error;
|
|
|
+ if (ret != 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ret = resize_hpt_rehash(resize);
|
|
|
+ if (ret != 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ resize_hpt_pivot(resize);
|
|
|
+
|
|
|
+out:
|
|
|
+ /* Let VCPUs run again */
|
|
|
+ kvm->arch.hpte_setup_done = 1;
|
|
|
+ smp_mb();
|
|
|
+out_no_hpt:
|
|
|
+ resize_hpt_release(kvm, resize);
|
|
|
+ mutex_unlock(&kvm->lock);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Functions for reading and writing the hash table via reads and
|
|
|
* writes on a file descriptor.
|