From 5e0785329a764034326de4690efedc6a9cb5d3bd Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Fri, 14 Apr 2023 16:35:40 -0700 Subject: [PATCH] ANDROID: gunyah: Sync with latest "gunyah: vm_mgr: Add framework for VM Functions" Align Gunyah VM Functions with Gunyah v13 patches: https://lore.kernel.org/all/20230509204801.2824351-18-quic_eberman@quicinc.com/ Bug: 279506910 Change-Id: Id8e043191539d41e4b54cb579ba2a84db76e0f70 Signed-off-by: Elliot Berman --- drivers/virt/gunyah/gunyah_ioeventfd.c | 17 +++- drivers/virt/gunyah/gunyah_irqfd.c | 15 +++- drivers/virt/gunyah/gunyah_vcpu.c | 14 +++- drivers/virt/gunyah/vm_mgr.c | 112 ++++++++++++++----------- include/linux/gunyah_vm_mgr.h | 28 +++++-- 5 files changed, 125 insertions(+), 61 deletions(-) diff --git a/drivers/virt/gunyah/gunyah_ioeventfd.c b/drivers/virt/gunyah/gunyah_ioeventfd.c index 517f55706ed9..f61291c17be5 100644 --- a/drivers/virt/gunyah/gunyah_ioeventfd.c +++ b/drivers/virt/gunyah/gunyah_ioeventfd.c @@ -111,7 +111,20 @@ static void gh_ioevent_unbind(struct gh_vm_function_instance *f) kfree(iofd); } -DECLARE_GH_VM_FUNCTION_INIT(ioeventfd, GH_FN_IOEVENTFD, - gh_ioeventfd_bind, gh_ioevent_unbind); +static bool gh_ioevent_compare(const struct gh_vm_function_instance *f, + const void *arg, size_t size) +{ + const struct gh_fn_ioeventfd_arg *instance = f->argp, + *other = arg; + + if (sizeof(*other) != size) + return false; + + return instance->addr == other->addr; +} + +DECLARE_GH_VM_FUNCTION_INIT(ioeventfd, GH_FN_IOEVENTFD, 3, + gh_ioeventfd_bind, gh_ioevent_unbind, + gh_ioevent_compare); MODULE_DESCRIPTION("Gunyah ioeventfds"); MODULE_LICENSE("GPL"); diff --git a/drivers/virt/gunyah/gunyah_irqfd.c b/drivers/virt/gunyah/gunyah_irqfd.c index 38e5fe266b00..7629e5777137 100644 --- a/drivers/virt/gunyah/gunyah_irqfd.c +++ b/drivers/virt/gunyah/gunyah_irqfd.c @@ -159,6 +159,19 @@ static void gh_irqfd_unbind(struct gh_vm_function_instance *f) kfree(irqfd); } -DECLARE_GH_VM_FUNCTION_INIT(irqfd, GH_FN_IRQFD, gh_irqfd_bind, gh_irqfd_unbind); +static bool gh_irqfd_compare(const struct gh_vm_function_instance *f, + const void *arg, size_t size) +{ + const struct gh_fn_irqfd_arg *instance = f->argp, + *other = arg; + + if (sizeof(*other) != size) + return false; + + return instance->label == other->label; +} + +DECLARE_GH_VM_FUNCTION_INIT(irqfd, GH_FN_IRQFD, 2, gh_irqfd_bind, gh_irqfd_unbind, + gh_irqfd_compare); MODULE_DESCRIPTION("Gunyah irqfds"); MODULE_LICENSE("GPL"); diff --git a/drivers/virt/gunyah/gunyah_vcpu.c b/drivers/virt/gunyah/gunyah_vcpu.c index f8925b77851a..b6692afca3de 100644 --- a/drivers/virt/gunyah/gunyah_vcpu.c +++ b/drivers/virt/gunyah/gunyah_vcpu.c @@ -457,6 +457,18 @@ static void gh_vcpu_unbind(struct gh_vm_function_instance *f) kref_put(&vcpu->kref, vcpu_release); } -DECLARE_GH_VM_FUNCTION_INIT(vcpu, GH_FN_VCPU, gh_vcpu_bind, gh_vcpu_unbind); +static bool gh_vcpu_compare(const struct gh_vm_function_instance *f, + const void *arg, size_t size) +{ + const struct gh_fn_vcpu_arg *instance = f->argp, + *other = arg; + + if (sizeof(*other) != size) + return false; + + return instance->id == other->id; +} + +DECLARE_GH_VM_FUNCTION_INIT(vcpu, GH_FN_VCPU, 1, gh_vcpu_bind, gh_vcpu_unbind, gh_vcpu_compare); MODULE_DESCRIPTION("Gunyah vCPU Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c index a6f2d5ee68e2..fe546220b7c1 100644 --- a/drivers/virt/gunyah/vm_mgr.c +++ b/drivers/virt/gunyah/vm_mgr.c @@ -19,47 +19,27 @@ #include "vm_mgr.h" -static DEFINE_XARRAY(functions); +static void gh_vm_free(struct work_struct *work); -int gh_vm_function_register(struct gh_vm_function *fn) +static DEFINE_XARRAY(gh_vm_functions); + +static void gh_vm_put_function(struct gh_vm_function *fn) { - if (!fn->bind || !fn->unbind) - return -EINVAL; - - return xa_err(xa_store(&functions, fn->type, fn, GFP_KERNEL)); + module_put(fn->mod); } -EXPORT_SYMBOL_GPL(gh_vm_function_register); - -static void gh_vm_remove_function_instance(struct gh_vm_function_instance *inst) - __must_hold(&inst->ghvm->fn_lock) -{ - inst->fn->unbind(inst); - list_del(&inst->vm_list); - module_put(inst->fn->mod); - kfree(inst->argp); - kfree(inst); -} - -void gh_vm_function_unregister(struct gh_vm_function *fn) -{ - /* Expecting unregister to only come when unloading a module */ - WARN_ON(fn->mod && module_refcount(fn->mod)); - xa_erase(&functions, fn->type); -} -EXPORT_SYMBOL_GPL(gh_vm_function_unregister); static struct gh_vm_function *gh_vm_get_function(u32 type) { struct gh_vm_function *fn; int r; - fn = xa_load(&functions, type); + fn = xa_load(&gh_vm_functions, type); if (!fn) { r = request_module("ghfunc:%d", type); if (r) - return ERR_PTR(r); + return ERR_PTR(r > 0 ? -r : r); - fn = xa_load(&functions, type); + fn = xa_load(&gh_vm_functions, type); } if (!fn || !try_module_get(fn->mod)) @@ -68,14 +48,36 @@ static struct gh_vm_function *gh_vm_get_function(u32 type) return fn; } -static long gh_vm_add_function(struct gh_vm *ghvm, struct gh_fn_desc *f) +static void gh_vm_remove_function_instance(struct gh_vm_function_instance *inst) + __must_hold(&inst->ghvm->fn_lock) +{ + inst->fn->unbind(inst); + list_del(&inst->vm_list); + gh_vm_put_function(inst->fn); + kfree(inst->argp); + kfree(inst); +} + +static void gh_vm_remove_functions(struct gh_vm *ghvm) +{ + struct gh_vm_function_instance *inst, *iiter; + + mutex_lock(&ghvm->fn_lock); + list_for_each_entry_safe(inst, iiter, &ghvm->functions, vm_list) { + gh_vm_remove_function_instance(inst); + } + mutex_unlock(&ghvm->fn_lock); +} + +static long gh_vm_add_function_instance(struct gh_vm *ghvm, struct gh_fn_desc *f) { struct gh_vm_function_instance *inst; void __user *argp; long r = 0; if (f->arg_size > GH_FN_MAX_ARG_SIZE) { - dev_err(ghvm->parent, "%s: arg_size > %d\n", __func__, GH_FN_MAX_ARG_SIZE); + dev_err_ratelimited(ghvm->parent, "%s: arg_size > %d\n", + __func__, GH_FN_MAX_ARG_SIZE); return -EINVAL; } @@ -110,7 +112,8 @@ static long gh_vm_add_function(struct gh_vm *ghvm, struct gh_fn_desc *f) mutex_lock(&ghvm->fn_lock); r = inst->fn->bind(inst); if (r < 0) { - module_put(inst->fn->mod); + mutex_unlock(&ghvm->fn_lock); + gh_vm_put_function(inst->fn); goto free_arg; } @@ -125,7 +128,7 @@ free: return r; } -static long gh_vm_rm_function(struct gh_vm *ghvm, struct gh_fn_desc *f) +static long gh_vm_rm_function_instance(struct gh_vm *ghvm, struct gh_fn_desc *f) { struct gh_vm_function_instance *inst, *iter; void __user *user_argp; @@ -150,11 +153,13 @@ static long gh_vm_rm_function(struct gh_vm *ghvm, struct gh_fn_desc *f) goto out; } + r = -ENOENT; list_for_each_entry_safe(inst, iter, &ghvm->functions, vm_list) { if (inst->fn->type == f->type && - f->arg_size == inst->arg_size && - !memcmp(argp, inst->argp, f->arg_size)) + inst->fn->compare(inst, argp, f->arg_size)) { gh_vm_remove_function_instance(inst); + r = 0; + } } kfree(argp); @@ -165,6 +170,23 @@ out: return r; } +int gh_vm_function_register(struct gh_vm_function *fn) +{ + if (!fn->bind || !fn->unbind) + return -EINVAL; + + return xa_err(xa_store(&gh_vm_functions, fn->type, fn, GFP_KERNEL)); +} +EXPORT_SYMBOL_GPL(gh_vm_function_register); + +void gh_vm_function_unregister(struct gh_vm_function *fn) +{ + /* Expecting unregister to only come when unloading a module */ + WARN_ON(fn->mod && module_refcount(fn->mod)); + xa_erase(&gh_vm_functions, fn->type); +} +EXPORT_SYMBOL_GPL(gh_vm_function_unregister); + int gh_vm_add_resource_ticket(struct gh_vm *ghvm, struct gh_vm_resource_ticket *ticket) { struct gh_vm_resource_ticket *iter; @@ -189,7 +211,7 @@ int gh_vm_add_resource_ticket(struct gh_vm *ghvm, struct gh_vm_resource_ticket * list_for_each_entry(ghrsc, &ghvm->resources, list) { if (ghrsc->type == ticket->resource_type && ghrsc->rm_label == ticket->label) { - if (!ticket->populate(ticket, ghrsc)) + if (ticket->populate(ticket, ghrsc)) list_move(&ghrsc->list, &ticket->resources); } } @@ -395,7 +417,6 @@ static void gh_vm_stop(struct gh_vm *ghvm) static void gh_vm_free(struct work_struct *work) { struct gh_vm *ghvm = container_of(work, struct gh_vm, free_work); - struct gh_vm_function_instance *inst, *iiter; struct gh_vm_resource_ticket *ticket, *titer; struct gh_resource *ghrsc, *riter; struct gh_vm_mem *mapping, *tmp; @@ -407,11 +428,7 @@ static void gh_vm_free(struct work_struct *work) fallthrough; case GH_RM_VM_STATUS_INIT_FAILED: case GH_RM_VM_STATUS_EXITED: - mutex_lock(&ghvm->fn_lock); - list_for_each_entry_safe(inst, iiter, &ghvm->functions, vm_list) { - gh_vm_remove_function_instance(inst); - } - mutex_unlock(&ghvm->fn_lock); + gh_vm_remove_functions(ghvm); mutex_lock(&ghvm->resources_lock); if (!list_empty(&ghvm->resource_tickets)) { @@ -728,21 +745,16 @@ static long gh_vm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_from_user(&f, argp, sizeof(f))) return -EFAULT; - r = gh_vm_add_function(ghvm, &f); + r = gh_vm_add_function_instance(ghvm, &f); break; } case GH_VM_REMOVE_FUNCTION: { - struct gh_fn_desc *f; + struct gh_fn_desc f; - f = kzalloc(sizeof(*f), GFP_KERNEL); - if (!f) - return -ENOMEM; - - if (copy_from_user(f, argp, sizeof(*f))) + if (copy_from_user(&f, argp, sizeof(f))) return -EFAULT; - r = gh_vm_rm_function(ghvm, f); - kfree(f); + r = gh_vm_rm_function_instance(ghvm, &f); break; } default: diff --git a/include/linux/gunyah_vm_mgr.h b/include/linux/gunyah_vm_mgr.h index 2dbf5e5f4037..1527861a5c63 100644 --- a/include/linux/gunyah_vm_mgr.h +++ b/include/linux/gunyah_vm_mgr.h @@ -27,6 +27,7 @@ struct gh_vm_function { struct module *mod; long (*bind)(struct gh_vm_function_instance *f); void (*unbind)(struct gh_vm_function_instance *f); + bool (*compare)(const struct gh_vm_function_instance *f, const void *arg, size_t size); }; /** @@ -53,22 +54,35 @@ struct gh_vm_function_instance { int gh_vm_function_register(struct gh_vm_function *f); void gh_vm_function_unregister(struct gh_vm_function *f); -#define DECLARE_GH_VM_FUNCTION(_name, _type, _bind, _unbind) \ - static struct gh_vm_function _name = { \ +/* Since the function identifiers were setup in a uapi header as an + * enum and we do no want to change that, the user must supply the expanded + * constant as well and the compiler checks they are the same. + * See also MODULE_ALIAS_RDMA_NETLINK. + */ +#define MODULE_ALIAS_GH_VM_FUNCTION(_type, _idx) \ + static inline void __maybe_unused __chk##_idx(void) \ + { \ + BUILD_BUG_ON(_type != _idx); \ + } \ + MODULE_ALIAS("ghfunc:" __stringify(_idx)) + +#define DECLARE_GH_VM_FUNCTION(_name, _type, _bind, _unbind, _compare) \ + static struct gh_vm_function _name = { \ .type = _type, \ .name = __stringify(_name), \ .mod = THIS_MODULE, \ .bind = _bind, \ .unbind = _unbind, \ - }; \ - MODULE_ALIAS("ghfunc:"__stringify(_type)) + .compare = _compare, \ + } #define module_gh_vm_function(__gf) \ module_driver(__gf, gh_vm_function_register, gh_vm_function_unregister) -#define DECLARE_GH_VM_FUNCTION_INIT(_name, _type, _bind, _unbind) \ - DECLARE_GH_VM_FUNCTION(_name, _type, _bind, _unbind); \ - module_gh_vm_function(_name) +#define DECLARE_GH_VM_FUNCTION_INIT(_name, _type, _idx, _bind, _unbind, _compare) \ + DECLARE_GH_VM_FUNCTION(_name, _type, _bind, _unbind, _compare); \ + module_gh_vm_function(_name); \ + MODULE_ALIAS_GH_VM_FUNCTION(_type, _idx) struct gh_vm_resource_ticket { struct list_head list; /* for gh_vm's resources list */