#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)
        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.