I don't really like the idea of having other modules register hypercalls. The hypercall address space is fixed and should be maintained by KVM.
I think things like virtio are rare and important enough, that it warrants having kvm.ko know enough about them to provide a higher level registration mechanism. The virtio network driver should register the fact that it's a virtio device, not which hypercalls it cares about. I'm not sure you gain anything by doing the virtio infrastructure in a module either. If anything, a config option in kvm.ko would suffice. Regards, Anthony Liguori On Fri, 2007-08-24 at 16:59 -0700, Dor Laor wrote: > New kernel modules for KVM are upcoming soon, these module > will need to use hypercalls. Before calling the hypercall function, > the kvm_main core module has to make sure it won't get unloaded. > So hypercall register/unregister are added. > > Except that the kernel hypercalls handlers are numberes > 0-KVM_NR_HYPERCALLS. > All the userspace handlers are above KVM_NR_HYPERCALLS. > > Signed-off-by: Dor Laor <[EMAIL PROTECTED]> > --- > drivers/kvm/kvm.h | 9 ++ > drivers/kvm/kvm_main.c | 223 > ++++++++++++++++++++++++++++++++++++++-------- > include/linux/kvm_para.h | 4 +- > 3 files changed, 197 insertions(+), 39 deletions(-) > mode change 100644 => 100755 drivers/kvm/kvm_main.c > > diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h > index a42a6f3..37240cf 100644 > --- a/drivers/kvm/kvm.h > +++ b/drivers/kvm/kvm.h > @@ -472,6 +472,12 @@ struct kvm_arch_ops { > unsigned char *hypercall_addr); > }; > > +struct kvm_hypercall { > + unsigned long (*hypercall)(struct kvm_vcpu*, unsigned long > args[]); > + struct module *module; > + int idx; > +}; > + > extern struct kvm_arch_ops *kvm_arch_ops; > > /* The guest did something we don't support. */ > @@ -588,6 +594,9 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu); > void kvm_mmu_unload(struct kvm_vcpu *vcpu); > > int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run); > +int kvm_register_hypercall(struct module *module, struct kvm_hypercall > *hypercall); > +int kvm_unregister_hypercall(struct kvm_hypercall *hypercall); > + > > static inline int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, > u32 error_code) > diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c > old mode 100644 > new mode 100755 > index abd7498..051b47a > --- a/drivers/kvm/kvm_main.c > +++ b/drivers/kvm/kvm_main.c > @@ -20,6 +20,7 @@ > #include "segment_descriptor.h" > > #include <linux/kvm.h> > +#include <linux/kvm_para.h> > #include <linux/module.h> > #include <linux/errno.h> > #include <linux/percpu.h> > @@ -84,6 +85,23 @@ static struct kvm_stats_debugfs_item { > > static struct dentry *debugfs_dir; > > +static struct kvm_hypercall hypercalls[KVM_KERNEL_NR_HYPERCALLS]; > + > +static int test_hypercall(struct kvm_vcpu *vcpu, unsigned long args[]) > +{ > + printk(KERN_DEBUG "%s: hypercall invoked\n", __FUNCTION__); > + > + return 0; > +} > + > +static struct kvm_hypercall __hypercall_test = { > + (unsigned long (*)(struct kvm_vcpu*, unsigned long > args[]))test_hypercall, > + THIS_MODULE, > + __NR_hypercall_test, > +}; > + > +static atomic_t dev_kvm_open_count = ATOMIC_INIT(0); > + > #define MAX_IO_MSRS 256 > > #define CR0_RESERVED_BITS > \ > @@ -302,6 +320,24 @@ static struct kvm *kvm_create_vm(void) > return kvm; > } > > +static int kvm_dev_open(struct inode *inode, struct file *filp) > +{ > + int ret = 0; > + > + if (atomic_inc_return(&dev_kvm_open_count) == 1) { > + ret = kvm_register_hypercall(THIS_MODULE, > &__hypercall_test); > + if (ret < 0) { > + printk(KERN_DEBUG "%s: kvm_register_hypercall > error, " > + "hypercall: %s\n", > + __FUNCTION__, "test"); > + goto out_test; > + } > + } > + > +out_test: > + return ret; > +} > + > /* > * Free any memory in @free but not in @dont. > */ > @@ -371,6 +407,20 @@ static void kvm_free_vcpus(struct kvm *kvm) > > } > > +static int kvm_dev_release(struct inode *inode, struct file *filp) > +{ > + int ret = 0; > + > + atomic_dec(&dev_kvm_open_count); > + ret = kvm_unregister_hypercall(&__hypercall_test); > + if (ret < 0) { > + printk(KERN_DEBUG "%s:kvm_unregister_hypercall error " > + "hypercall: %s\n", __FUNCTION__, > "hypercall_test"); > + } > + > + return ret; > +} > + > static void kvm_destroy_vm(struct kvm *kvm) > { > spin_lock(&kvm_lock); > @@ -1267,50 +1317,145 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu) > } > EXPORT_SYMBOL_GPL(kvm_emulate_halt); > > -int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) > +int kvm_register_hypercall(struct module* module, struct kvm_hypercall > *hypercall) > { > - unsigned long nr, a0, a1, a2, a3, a4, a5, ret; > + int r = 0; > > - kvm_arch_ops->cache_regs(vcpu); > - ret = -KVM_EINVAL; > -#ifdef CONFIG_X86_64 > - if (is_long_mode(vcpu)) { > - nr = vcpu->regs[VCPU_REGS_RAX]; > - a0 = vcpu->regs[VCPU_REGS_RDI]; > - a1 = vcpu->regs[VCPU_REGS_RSI]; > - a2 = vcpu->regs[VCPU_REGS_RDX]; > - a3 = vcpu->regs[VCPU_REGS_RCX]; > - a4 = vcpu->regs[VCPU_REGS_R8]; > - a5 = vcpu->regs[VCPU_REGS_R9]; > - } else > -#endif > - { > + if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS || > + hypercall->idx < 0) { > + printk(KERN_DEBUG "%s:hypercall registration idx(%d)\n", > + __FUNCTION__, hypercall->idx); > + return -EINVAL; > + } > + > + spin_lock(&kvm_lock); > + > + if (hypercalls[hypercall->idx].hypercall) { > + printk(KERN_DEBUG "%s:hypercall idx(%d) already > taken\n", > + __FUNCTION__, hypercall->idx); > + r = -EEXIST; > + goto out; > + } > + > + if (try_module_get(module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + r = -EINVAL; > + goto out; > + } > + > + hypercalls[hypercall->idx].hypercall = hypercall->hypercall; > + hypercalls[hypercall->idx].module = module; > + > +out: > + spin_unlock(&kvm_lock); > + > + return r; > +} > +EXPORT_SYMBOL_GPL(kvm_register_hypercall); > + > +int kvm_unregister_hypercall(struct kvm_hypercall *hypercall) > +{ > + if (hypercall->idx >= KVM_KERNEL_NR_HYPERCALLS || > + hypercall->idx < 0) { > + printk(KERN_DEBUG "%s:hypercall unregistration > idx(%d)\n", > + __FUNCTION__, hypercall->idx); > + return -EINVAL; > + } > + > + spin_lock(&kvm_lock); > + if (!hypercalls[hypercall->idx].hypercall) { > + printk(KERN_DEBUG "%s:hypercall idx(%d) was not > registered\n", > + __FUNCTION__, hypercall->idx); > + spin_unlock(&kvm_lock); > + return -EEXIST; > + } > + > + hypercalls[hypercall->idx].hypercall = 0; > + module_put(hypercalls[hypercall->idx].module); > + spin_unlock(&kvm_lock); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(kvm_unregister_hypercall); > + > +/* > + * Generic hypercall dispatcher routine. > + * Returns 0 for user space handling, 1 on success handling > + */ > + int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) > + { > + unsigned long nr, ret; > + unsigned long args[6]; > + > + kvm_arch_ops->cache_regs(vcpu); > + ret = -KVM_EINVAL; > + #ifdef CONFIG_X86_64 > + if (is_long_mode(vcpu)) { > + nr = vcpu->regs[VCPU_REGS_RAX]; > + args[0] = vcpu->regs[VCPU_REGS_RDI]; > + args[1] = vcpu->regs[VCPU_REGS_RSI]; > + args[2] = vcpu->regs[VCPU_REGS_RDX]; > + args[3] = vcpu->regs[VCPU_REGS_RCX]; > + args[4] = vcpu->regs[VCPU_REGS_R8]; > + args[5] = vcpu->regs[VCPU_REGS_R9]; > + } else > + #endif > + { > nr = vcpu->regs[VCPU_REGS_RBX] & -1u; > - a0 = vcpu->regs[VCPU_REGS_RAX] & -1u; > - a1 = vcpu->regs[VCPU_REGS_RCX] & -1u; > - a2 = vcpu->regs[VCPU_REGS_RDX] & -1u; > - a3 = vcpu->regs[VCPU_REGS_RSI] & -1u; > - a4 = vcpu->regs[VCPU_REGS_RDI] & -1u; > - a5 = vcpu->regs[VCPU_REGS_RBP] & -1u; > - } > - switch (nr) { > - default: > - run->hypercall.nr = nr; > - run->hypercall.args[0] = a0; > - run->hypercall.args[1] = a1; > - run->hypercall.args[2] = a2; > - run->hypercall.args[3] = a3; > - run->hypercall.args[4] = a4; > - run->hypercall.args[5] = a5; > - run->hypercall.ret = ret; > - run->hypercall.longmode = is_long_mode(vcpu); > - kvm_arch_ops->decache_regs(vcpu); > + args[0] = vcpu->regs[VCPU_REGS_RAX] & -1u; > + args[1] = vcpu->regs[VCPU_REGS_RCX] & -1u; > + args[2] = vcpu->regs[VCPU_REGS_RDX] & -1u; > + args[3] = vcpu->regs[VCPU_REGS_RSI] & -1u; > + args[4] = vcpu->regs[VCPU_REGS_RDI] & -1u; > + args[5] = vcpu->regs[VCPU_REGS_RBP] & -1u; > + } > + > + if (nr >= KVM_KERNEL_NR_HYPERCALLS || nr < 0) { > + run->hypercall.nr = nr; > + memcpy(run->hypercall.args, args, sizeof(args)); > + run->hypercall.ret = 0; > + run->hypercall.longmode = is_long_mode(vcpu); > + kvm_arch_ops->decache_regs(vcpu); > run->exit_reason = KVM_EXIT_HYPERCALL; > > - return 0; > + return 0; > + } > + > + /* The hypercall might block or do intensive work */ > + vcpu_put(vcpu); > + > + /* > + * Increase the hypercall's module ref count assures > + * that no one will remove the module while a hypercall > + * is executing. > + * Theoretically we need to lock to get coherent > module-hypercall > + * view but practically there's almost no users this mechanism > now. > + */ > + if (try_module_get(hypercalls[nr].module) < 0) { > + printk(KERN_DEBUG "%s: module reference count++ > failed\n", > + __FUNCTION__); > + ret = -EINVAL; > + goto out; > } > - vcpu->regs[VCPU_REGS_RAX] = ret; > - kvm_arch_ops->decache_regs(vcpu); > + > + if (!hypercalls[nr].hypercall) { > + printk(KERN_ERR "%s: hypercall nr(%lx) was not yet > registered\n", > + __FUNCTION__, nr); > + ret = -EINVAL; > + goto out_put; > + } > + > + ret = hypercalls[nr].hypercall(vcpu, args); > + > +out_put: > + module_put(hypercalls[nr].module); > +out: > + vcpu_load(vcpu); > + > + vcpu->regs[VCPU_REGS_RAX] = ret; > + kvm_arch_ops->decache_regs(vcpu); > + > return 1; > } > EXPORT_SYMBOL_GPL(kvm_hypercall); > @@ -2847,6 +2992,8 @@ out: > } > > static struct file_operations kvm_chardev_ops = { > + .open = kvm_dev_open, > + .release = kvm_dev_release, > .unlocked_ioctl = kvm_dev_ioctl, > .compat_ioctl = kvm_dev_ioctl, > }; > diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h > index 754e29d..0cf516a 100644 > --- a/include/linux/kvm_para.h > +++ b/include/linux/kvm_para.h > @@ -71,6 +71,8 @@ struct kvm_vcpu_para_state { > * No registers are clobbered by the hypercall, except that the > * return value is in RAX. > */ > -#define __NR_hypercalls 0 > + > +#define KVM_KERNEL_NR_HYPERCALLS 1 > +#define __NR_hypercall_test 0 > > #endif > > > ----- > In simplicity there is elegance. > Dor Laor ;) > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Splunk Inc. > Still grepping through log files to find problems? Stop. > Now Search log events and configuration files using AJAX and a browser. > Download your FREE copy of Splunk now >> http://get.splunk.com/ > _______________________________________________ > kvm-devel mailing list > kvm-devel@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/kvm-devel ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel