From e98e9702f59e24a9937abf6c9b17cf89960e6a2f Mon Sep 17 00:00:00 2001 From: Murali Nalajala Date: Mon, 15 Jun 2020 14:56:18 -0700 Subject: [PATCH] haven: irq_lend: add support for IRQ notifications Currently IRQ APIs are combined with notifications to VMs for sharing the handles. This limit the clients for managing the error handling from the drivers. Hence separate the notifications from the actual APIs so that clients manage the error handling better. Add support for notifications like IRQ_LEND/ACCEPT/RELEASE from SVM to PVM when SVM accept the irq handle shared by PVM. Change-Id: I49999c2d9494c68b1542680e521c012a25e3f6a8 Signed-off-by: Murali Nalajala --- .../focaltech_touch/focaltech_core.c | 19 +- drivers/input/touchscreen/st/fts.c | 22 +- drivers/virt/haven/hh_irq_lend.c | 215 ++++++++++++---- drivers/virt/haven/hh_rm_core.c | 9 + drivers/virt/haven/hh_rm_drv_private.h | 1 + drivers/virt/haven/hh_rm_iface.c | 231 +++++++++--------- include/linux/haven/hh_irq_lend.h | 13 +- include/linux/haven/hh_rm_drv.h | 40 ++- 8 files changed, 375 insertions(+), 175 deletions(-) diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c index cf38fd5a7766..f49dc583ea66 100644 --- a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -296,7 +296,9 @@ static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data) return rc; } -static void fts_ts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label) +static void fts_ts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_data *fts_data = data; struct irq_data *irq_data; @@ -472,6 +474,10 @@ static void fts_ts_trusted_touch_vm_mode_disable(struct fts_ts_data *fts_data) pr_err("Failed to release irq rc:%d\n", rc); else atomic_set(&fts_data->vm_info->tvm_owns_irq, 0); + + rc = hh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); } atomic_set(&fts_data->trusted_touch_enabled, 0); reinit_completion(&fts_data->resource_checkpoint); @@ -600,6 +606,7 @@ static void fts_ts_trusted_touch_complete(struct fts_ts_data *fts_data) } static void fts_ts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, enum hh_irq_label label) { struct fts_ts_data *fts_data = data; @@ -730,7 +737,7 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data) } atomic_set(&vm_info->pvm_owns_iomem, 0); - rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name, + rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data); if (rc) { pr_err("Failed to lend irq\n"); @@ -738,6 +745,12 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data) } atomic_set(&vm_info->pvm_owns_irq, 0); + rc = hh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + reinit_completion(&fts_data->trusted_touch_powerdown); atomic_set(&fts_data->trusted_touch_enabled, 1); pr_debug("trusted touch enabled\n"); @@ -809,7 +822,7 @@ static int fts_ts_vm_init(struct fts_ts_data *fts_data) goto init_fail; } vm_info->mem_cookie = mem_cookie; - rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM, + rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM, &fts_ts_vm_irq_on_lend_callback, fts_data); atomic_set(&vm_info->tvm_owns_irq, 0); atomic_set(&vm_info->tvm_owns_iomem, 0); diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 3832d89422bf..21c12f74fb99 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -321,7 +321,9 @@ static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) return rc; } -static void fts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label) +static void fts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_info *info = data; struct irq_data *irq_data; @@ -497,6 +499,10 @@ static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info) pr_err("Failed to release irq rc:%d\n", rc); else atomic_set(&info->vm_info->tvm_owns_irq, 0); + + rc = hh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); } atomic_set(&info->trusted_touch_enabled, 0); reinit_completion(&info->resource_checkpoint); @@ -624,7 +630,9 @@ static void fts_trusted_touch_complete(struct fts_ts_info *info) } } -static void fts_vm_irq_on_release_callback(void *data, enum hh_irq_label label) +static void fts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_info *info = data; int rc = 0; @@ -755,7 +763,7 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) } atomic_set(&vm_info->pvm_owns_iomem, 0); - rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name, + rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, info->client->irq, &fts_vm_irq_on_release_callback, info); if (rc) { pr_err("Failed to lend irq\n"); @@ -763,6 +771,12 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) } atomic_set(&vm_info->pvm_owns_irq, 0); + rc = hh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + reinit_completion(&info->trusted_touch_powerdown); atomic_set(&info->trusted_touch_enabled, 1); pr_debug("trusted touch enabled\n"); @@ -833,7 +847,7 @@ static int fts_vm_init(struct fts_ts_info *info) goto init_fail; } vm_info->mem_cookie = mem_cookie; - rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM, + rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM, &fts_vm_irq_on_lend_callback, info); atomic_set(&vm_info->tvm_owns_irq, 0); atomic_set(&vm_info->tvm_owns_iomem, 0); diff --git a/drivers/virt/haven/hh_irq_lend.c b/drivers/virt/haven/hh_irq_lend.c index 76fd1f7fdc58..9e4a7fb4d354 100644 --- a/drivers/virt/haven/hh_irq_lend.c +++ b/drivers/virt/haven/hh_irq_lend.c @@ -17,22 +17,20 @@ struct hh_irq_entry { hh_vmid_t vmid; enum hh_vm_names vm_name; + hh_irq_handle_fn_v2 v2_handle; hh_irq_handle_fn handle; void *data; enum { - /* start state */ HH_IRQ_STATE_NONE, - /* NONE -> WAIT_RELEASE by hh_irq_lend */ - HH_IRQ_STATE_WAIT_RELEASE, - /* NONE -> WAIT_LEND by hh_irq_wait_lend */ + + HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT, HH_IRQ_STATE_WAIT_LEND, - /* WAIT_RELEASE -> RELEASED by notifier */ - /* RELEASED -> NONE by hh_irq_reclaim */ - HH_IRQ_STATE_RELEASED, - /* WAIT_LEND -> LENT by notifier */ - /* LENT -> NONE by hh_irq_release */ - HH_IRQ_STATE_LENT, + + /* notification states */ + HH_IRQ_STATE_RELEASED, /* svm -> pvm */ + HH_IRQ_STATE_ACCEPTED, /* svm -> pvm */ + HH_IRQ_STATE_LENT, /* pvm -> svm */ } state; hh_virq_handle_t virq_handle; }; @@ -40,31 +38,50 @@ struct hh_irq_entry { static struct hh_irq_entry hh_irq_entries[HH_IRQ_LABEL_MAX]; static DEFINE_SPINLOCK(hh_irq_lend_lock); -static int hh_irq_released_nb_handler(struct notifier_block *this, +static int hh_irq_released_accepted_nb_handler(struct notifier_block *this, unsigned long cmd, void *data) { unsigned long flags; enum hh_irq_label label; struct hh_irq_entry *entry; - struct hh_rm_notif_vm_irq_released_payload *released = data; + struct hh_rm_notif_vm_irq_released_payload *released; + struct hh_rm_notif_vm_irq_accepted_payload *accepted; - if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED) + if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED && + cmd != HH_RM_NOTIF_VM_IRQ_ACCEPTED) return NOTIFY_DONE; spin_lock_irqsave(&hh_irq_lend_lock, flags); for (label = 0; label < HH_IRQ_LABEL_MAX; label++) { entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_WAIT_RELEASE) + + if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT && + entry->state != HH_IRQ_STATE_ACCEPTED) continue; - if (released->virq_handle == entry->virq_handle) { - entry->state = HH_IRQ_STATE_RELEASED; - spin_unlock_irqrestore(&hh_irq_lend_lock, - flags); + switch (cmd) { + case HH_RM_NOTIF_VM_IRQ_RELEASED: + released = data; + if (released->virq_handle == entry->virq_handle) { + entry->state = HH_IRQ_STATE_RELEASED; + spin_unlock_irqrestore(&hh_irq_lend_lock, + flags); + entry->v2_handle(entry->data, cmd, label); + return NOTIFY_OK; + } - entry->handle(entry->data, label); + break; + case HH_RM_NOTIF_VM_IRQ_ACCEPTED: + accepted = data; + if (accepted->virq_handle == entry->virq_handle) { + entry->state = HH_IRQ_STATE_ACCEPTED; + spin_unlock_irqrestore(&hh_irq_lend_lock, + flags); + entry->v2_handle(entry->data, cmd, label); + return NOTIFY_OK; + } - return NOTIFY_OK; + break; } } spin_unlock_irqrestore(&hh_irq_lend_lock, flags); @@ -72,8 +89,8 @@ static int hh_irq_released_nb_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block hh_irq_released_nb = { - .notifier_call = hh_irq_released_nb_handler, +static struct notifier_block hh_irq_released_accepted_nb = { + .notifier_call = hh_irq_released_accepted_nb_handler, }; static int hh_irq_lent_nb_handler(struct notifier_block *this, @@ -112,7 +129,7 @@ static int hh_irq_lent_nb_handler(struct notifier_block *this, spin_unlock_irqrestore(&hh_irq_lend_lock, flags); - entry->handle(entry->data, label); + entry->v2_handle(entry->data, cmd, label); return NOTIFY_OK; } @@ -127,22 +144,24 @@ static struct notifier_block hh_irq_lent_nb = { }; /** - * hh_irq_lend: Lend a hardware interrupt to another VM + * hh_irq_lend_v2: Lend a hardware interrupt to another VM * @label: vIRQ high-level label * @name: VM name to send interrupt to * @irq: Linux IRQ number to lend - * @on_release: callback to invoke when other VM returns the - * interrupt - * @data: Argument to pass to on_release + * @cb_handle: callback to invoke when other VM release or accept the interrupt + * @data: Argument to pass to cb_handle + * + * Returns 0 on success also the handle corresponding to Linux IRQ#. + * Returns < 0 on error */ -int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, - int irq, hh_irq_handle_fn on_release, void *data) +int hh_irq_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + int irq, hh_irq_handle_fn_v2 cb_handle, void *data) { int ret, virq; unsigned long flags; struct hh_irq_entry *entry; - if (label >= HH_IRQ_LABEL_MAX || !on_release) + if (label >= HH_IRQ_LABEL_MAX || !cb_handle) return -EINVAL; entry = &hh_irq_entries[label]; @@ -163,16 +182,62 @@ int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, return ret; } - entry->handle = on_release; + entry->v2_handle = cb_handle; entry->data = data; - entry->state = HH_IRQ_STATE_WAIT_RELEASE; + entry->state = HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT; spin_unlock_irqrestore(&hh_irq_lend_lock, flags); - return hh_rm_vm_irq_lend_notify(entry->vmid, virq, label, - &entry->virq_handle); + return hh_rm_vm_irq_lend(entry->vmid, virq, label, &entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_lend_v2); + +/** + * hh_irq_lend: Lend a hardware interrupt to another VM + * @label: vIRQ high-level label + * @name: VM name to send interrupt to + * @irq: Linux IRQ number to lend + * @cb_handle: callback to invoke when other VM release or accept the interrupt + * @data: Argument to pass to cb_handle + * + * Returns 0 on success also the handle corresponding to Linux IRQ#. + * Returns < 0 on error + */ +int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, + int irq, hh_irq_handle_fn cb_handle, void *data) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX || !cb_handle) + return -EINVAL; + + entry = &hh_irq_entries[label]; + entry->handle = cb_handle; + + return 0; } EXPORT_SYMBOL(hh_irq_lend); +/** + * hh_irq_lend_notify: Pass the irq handle to other VM for accept + * @label: vIRQ high-level label + * + * Returns 0 on success, < 0 on error + */ +int hh_irq_lend_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + if (entry->state == HH_IRQ_STATE_NONE) + return -EINVAL; + + return hh_rm_vm_irq_lend_notify(entry->vmid, entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_lend_notify); + /** * hh_irq_reclaim: Reclaim a hardware interrupt after other VM * has released. @@ -193,7 +258,8 @@ int hh_irq_reclaim(enum hh_irq_label label) entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_RELEASED) + if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT && + (entry->state != HH_IRQ_STATE_RELEASED)) return -EINVAL; ret = hh_rm_vm_irq_reclaim(entry->virq_handle); @@ -204,15 +270,14 @@ int hh_irq_reclaim(enum hh_irq_label label) EXPORT_SYMBOL(hh_irq_reclaim); /** - * hh_irq_wait_lend: Register to claim a lent interrupt from another - * VM + * hh_irq_wait_for_lend_v2: Register to claim a lent interrupt from another VM * @label: vIRQ high-level label * @name: Lender's VM name. If don't care, then use HH_VM_MAX * @on_lend: callback to invoke when other VM lends the interrupt * @data: Argument to pass to on_lend */ -int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, - hh_irq_handle_fn on_lend, void *data) +int hh_irq_wait_for_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn_v2 on_lend, void *data) { unsigned long flags; struct hh_irq_entry *entry; @@ -229,13 +294,27 @@ int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, } entry->vm_name = name; - entry->handle = on_lend; + entry->v2_handle = on_lend; entry->data = data; entry->state = HH_IRQ_STATE_WAIT_LEND; spin_unlock_irqrestore(&hh_irq_lend_lock, flags); return 0; } +EXPORT_SYMBOL(hh_irq_wait_for_lend_v2); + +/** + * hh_irq_wait_lend: Register to claim a lent interrupt from another VM + * @label: vIRQ high-level label + * @name: Lender's VM name. If don't care, then use HH_VM_MAX + * @on_lend: callback to invoke when other VM lends the interrupt + * @data: Argument to pass to on_lend + */ +int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn on_lend, void *data) +{ + return 0; +} EXPORT_SYMBOL(hh_irq_wait_for_lend); /** @@ -276,10 +355,38 @@ int hh_irq_accept(enum hh_irq_label label, int irq, int type) if (irq == -1) irq = hh_rm_virq_to_irq(virq, type); + entry->state = HH_IRQ_STATE_ACCEPTED; return irq; } EXPORT_SYMBOL(hh_irq_accept); +/** + * hh_irq_accept_notify: Notify the lend vm (pvm) that IRQ is accepted + * @label: vIRQ high-level label + * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1 + * + * Returns the Linux IRQ# that vIRQ was registered to on success. + * Returns <0 on error + * This function is not thread-safe w.r.t. IRQ lend state. Do not race with + * hh_irq_release or another hh_irq_accept with same label. + */ +int hh_irq_accept_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + + if (entry->state != HH_IRQ_STATE_ACCEPTED) + return -EINVAL; + + return hh_rm_vm_irq_accept_notify(entry->vmid, + entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_accept_notify); + /** * hh_irq_release: Release a lent interrupt * @label: vIRQ high-level label @@ -296,17 +403,34 @@ int hh_irq_release(enum hh_irq_label label) entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_LENT) + if (entry->state != HH_IRQ_STATE_ACCEPTED) return -EINVAL; - ret = hh_rm_vm_irq_release_notify(entry->vmid, - entry->virq_handle); + ret = hh_rm_vm_irq_release(entry->virq_handle); if (!ret) entry->state = HH_IRQ_STATE_WAIT_LEND; return ret; } EXPORT_SYMBOL(hh_irq_release); +int hh_irq_release_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + + if (entry->state != HH_IRQ_STATE_ACCEPTED && + entry->state != HH_IRQ_STATE_WAIT_LEND) + return -EINVAL; + + return hh_rm_vm_irq_release_notify(entry->vmid, + entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_release_notify); + static int __init hh_irq_lend_init(void) { int ret; @@ -314,14 +438,15 @@ static int __init hh_irq_lend_init(void) ret = hh_rm_register_notifier(&hh_irq_lent_nb); if (ret) return ret; - return hh_rm_register_notifier(&hh_irq_released_nb); + + return hh_rm_register_notifier(&hh_irq_released_accepted_nb); } module_init(hh_irq_lend_init); static void hh_irq_lend_exit(void) { hh_rm_unregister_notifier(&hh_irq_lent_nb); - hh_rm_unregister_notifier(&hh_irq_released_nb); + hh_rm_unregister_notifier(&hh_irq_released_accepted_nb); } module_exit(hh_irq_lend_exit); diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index 152f9d78dbb8..67238a2819e0 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -218,6 +218,15 @@ static int hh_rm_process_notif(void *recv_buff, size_t recv_buff_size) goto err; } break; + case HH_RM_NOTIF_VM_IRQ_ACCEPTED: + if (recv_buff_size != sizeof(*hdr) + + sizeof(struct hh_rm_notif_vm_irq_accepted_payload)) { + pr_err("%s: Invalid size for VM_IRQ_ACCEPTED notif: %u\n", + __func__, recv_buff_size - sizeof(*hdr)); + ret = -EINVAL; + goto err; + } + break; case HH_RM_NOTIF_MEM_SHARED: if (recv_buff_size < sizeof(*hdr) + sizeof(struct hh_rm_notif_mem_shared_payload)) { diff --git a/drivers/virt/haven/hh_rm_drv_private.h b/drivers/virt/haven/hh_rm_drv_private.h index 36f990b24189..688e301b5779 100644 --- a/drivers/virt/haven/hh_rm_drv_private.h +++ b/drivers/virt/haven/hh_rm_drv_private.h @@ -181,6 +181,7 @@ struct hh_vm_irq_lend_resp_payload { /* Call: VM_IRQ_NOTIFY */ #define HH_VM_IRQ_NOTIFY_FLAGS_LENT BIT(0) #define HH_VM_IRQ_NOTIFY_FLAGS_RELEASED BIT(1) +#define HH_VM_IRQ_NOTIFY_FLAGS_ACCEPTED BIT(2) /* Call: VM_IRQ_RELEASE */ struct hh_vm_irq_release_req_payload { diff --git a/drivers/virt/haven/hh_rm_iface.c b/drivers/virt/haven/hh_rm_iface.c index ddcf75c384aa..1c4923c0288c 100644 --- a/drivers/virt/haven/hh_rm_iface.c +++ b/drivers/virt/haven/hh_rm_iface.c @@ -145,102 +145,6 @@ out: return resp_entries; } -/** - * hh_rm_vm_irq_accept: Bind the virq number to the supplied virq_handle - * @virq_handle: The virtual IRQ handle (for example, obtained via - * call to hh_rm_get_hyp_resources()) - * @virq: The virtual IRQ number to bind to. Note that this is the virtual - * GIC IRQ number and not the linux IRQ number. Pass -1 here if the - * caller wants the Resource Manager VM to allocate a number - * - * If provided -1 for virq, the function returns the new IRQ number, else - * the one that was already provided. - * - * The function encodes the error codes via ERR_PTR. Hence, the caller is - * responsible to check it with IS_ERR_OR_NULL(). - */ -int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) -{ - struct hh_vm_irq_accept_resp_payload *resp_payload; - struct hh_vm_irq_accept_req_payload req_payload = {0}; - size_t resp_payload_size; - int ret, reply_err_code; - - /* -1 is valid for virq if requesting for a new number */ - if (virq < -1) - return -EINVAL; - - req_payload.virq_handle = virq_handle; - req_payload.virq = virq; - - resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT, - &req_payload, sizeof(req_payload), - &resp_payload_size, &reply_err_code); - if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { - ret = PTR_ERR(resp_payload); - pr_err("%s: VM_IRQ_ACCEPT failed with err: %d\n", - __func__, ret); - return ret; - } - - if (virq == -1 && resp_payload_size != sizeof(*resp_payload)) { - pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n", - __func__, resp_payload_size); - ret = -EINVAL; - goto out; - } - - ret = virq == -1 ? resp_payload->virq : virq; -out: - kfree(resp_payload); - return ret; -} -EXPORT_SYMBOL(hh_rm_vm_irq_accept); - -/** - * hh_rm_vm_irq_lend: Lend an IRQ to another VM - * @vmid: VM to lend the interrupt to - * @virq: Virtual IRQ number to lend - * @label: Label to give to VM so it may know how to associate the interrupt - * @virq_handle: Response handle which RM will accept from the other VM to take - * the lent interrupt - */ -static int hh_rm_vm_irq_lend(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle) -{ - struct hh_vm_irq_lend_resp_payload *resp_payload; - struct hh_vm_irq_lend_req_payload req_payload = {0}; - size_t resp_payload_size; - int ret = 0, reply_err_code; - - req_payload.vmid = vmid; - req_payload.virq = virq; - req_payload.label = label; - - resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_LEND, - &req_payload, sizeof(req_payload), - &resp_payload_size, &reply_err_code); - if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { - ret = PTR_ERR(resp_payload); - pr_err("%s: VM_IRQ_LEND failed with err: %d\n", - __func__, ret); - return ret; - } - - if (resp_payload_size != sizeof(*resp_payload)) { - pr_err("%s: Invalid size received for VM_IRQ_LEND: %u\n", - __func__, resp_payload_size); - ret = -EINVAL; - goto out; - } - - if (virq_handle) - *virq_handle = resp_payload->virq; -out: - kfree(resp_payload); - return ret; -} - /** * hh_rm_vm_irq_notify: Notify an IRQ to another VM * @vmids: VMs to notify the handle about @@ -250,7 +154,7 @@ out: * the lent interrupt */ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, - u16 flags, hh_virq_handle_t virq_handle) + u16 flags, hh_virq_handle_t virq_handle) { void *resp; struct hh_vm_irq_notify_req_payload *req_payload; @@ -308,6 +212,51 @@ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, return ret; } +/** + * hh_rm_vm_irq_lend: Lend an IRQ to another VM + * @vmid: VM to lend the interrupt to + * @virq: Virtual IRQ number to lend + * @label: Label to give to VM so it may know how to associate the interrupt + * @virq_handle: Response handle which RM will accept from the other VM to take + * the lent interrupt + */ +int hh_rm_vm_irq_lend(hh_vmid_t vmid, int virq, int label, + hh_virq_handle_t *virq_handle) +{ + struct hh_vm_irq_lend_resp_payload *resp_payload; + struct hh_vm_irq_lend_req_payload req_payload = {0}; + size_t resp_payload_size; + int ret = 0, reply_err_code; + + req_payload.vmid = vmid; + req_payload.virq = virq; + req_payload.label = label; + + resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_LEND, + &req_payload, sizeof(req_payload), + &resp_payload_size, &reply_err_code); + if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { + ret = PTR_ERR(resp_payload); + pr_err("%s: VM_IRQ_LEND failed with err: %d\n", + __func__, ret); + return ret; + } + + if (resp_payload_size != sizeof(*resp_payload)) { + pr_err("%s: Invalid size received for VM_IRQ_LEND: %u\n", + __func__, resp_payload_size); + ret = -EINVAL; + goto out; + } + + if (virq_handle) + *virq_handle = resp_payload->virq; +out: + kfree(resp_payload); + return ret; +} +EXPORT_SYMBOL(hh_rm_vm_irq_lend); + /** * hh_rm_vm_irq_lend_notify: Lend an IRQ to a VM and notify the VM about it * @vmid: VM to lend interrupt to @@ -320,20 +269,10 @@ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, * This function performs interrupt sharing flow for "HLOS" described in * Resource Manager High Level Design Sec. 3.3.3. */ -int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle) +int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) { - int ret; - - if (!virq_handle) - return -EINVAL; - - ret = hh_rm_vm_irq_lend(vmid, virq, label, virq_handle); - if (ret) - return ret; - return hh_rm_vm_irq_notify(&vmid, 1, HH_VM_IRQ_NOTIFY_FLAGS_LENT, - *virq_handle); + virq_handle); } EXPORT_SYMBOL(hh_rm_vm_irq_lend_notify); @@ -341,7 +280,7 @@ EXPORT_SYMBOL(hh_rm_vm_irq_lend_notify); * hh_rm_vm_irq_release: Return a lent IRQ * @virq_handle: IRQ handle to be released */ -static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) +int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) { struct hh_vm_irq_release_req_payload req_payload = {0}; void *resp; @@ -374,6 +313,7 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) return ret; } +EXPORT_SYMBOL(hh_rm_vm_irq_release); /** * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has @@ -383,17 +323,76 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) */ int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) { - int ret; - - ret = hh_rm_vm_irq_release(virq_handle); - if (ret) - return ret; - return hh_rm_vm_irq_notify(NULL, 0, HH_VM_IRQ_NOTIFY_FLAGS_RELEASED, virq_handle); } EXPORT_SYMBOL(hh_rm_vm_irq_release_notify); +/** + * hh_rm_vm_irq_accept: Bind the virq number to the supplied virq_handle + * @virq_handle: The virtual IRQ handle (for example, obtained via + * call to hh_rm_get_hyp_resources()) + * @virq: The virtual IRQ number to bind to. Note that this is the virtual + * GIC IRQ number and not the linux IRQ number. Pass -1 here if the + * caller wants the Resource Manager VM to allocate a number + * + * If provided -1 for virq, the function returns the new IRQ number, else + * the one that was already provided. + * + * The function encodes the error codes via ERR_PTR. Hence, the caller is + * responsible to check it with IS_ERR_OR_NULL(). + */ +int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) +{ + struct hh_vm_irq_accept_resp_payload *resp_payload; + struct hh_vm_irq_accept_req_payload req_payload = {0}; + size_t resp_payload_size; + int ret, reply_err_code; + + /* -1 is valid for virq if requesting for a new number */ + if (virq < -1) + return -EINVAL; + + req_payload.virq_handle = virq_handle; + req_payload.virq = virq; + + resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT, + &req_payload, sizeof(req_payload), + &resp_payload_size, &reply_err_code); + if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { + ret = PTR_ERR(resp_payload); + pr_err("%s: VM_IRQ_ACCEPT failed with err: %d\n", + __func__, ret); + return ret; + } + + if (virq == -1 && resp_payload_size != sizeof(*resp_payload)) { + pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n", + __func__, resp_payload_size); + ret = -EINVAL; + goto out; + } + + ret = virq == -1 ? resp_payload->virq : virq; +out: + kfree(resp_payload); + return ret; +} +EXPORT_SYMBOL(hh_rm_vm_irq_accept); + +/** + * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has + * been released. + * @vmid: VM to release interrupt to + * @virq_handle: Virtual IRQ handle to release + */ +int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) +{ + return hh_rm_vm_irq_notify(NULL, 0, HH_VM_IRQ_NOTIFY_FLAGS_ACCEPTED, + virq_handle); +} +EXPORT_SYMBOL(hh_rm_vm_irq_accept_notify); + /** * hh_rm_vm_irq_reclaim: Return a lent IRQ * @virq_handle: IRQ handle to be reclaimed diff --git a/include/linux/haven/hh_irq_lend.h b/include/linux/haven/hh_irq_lend.h index f2737395b28d..afed74599cc9 100644 --- a/include/linux/haven/hh_irq_lend.h +++ b/include/linux/haven/hh_irq_lend.h @@ -19,14 +19,21 @@ enum hh_irq_label { }; typedef void (*hh_irq_handle_fn)(void *req, enum hh_irq_label label); - +typedef void (*hh_irq_handle_fn_v2)(void *req, unsigned long notif_type, + enum hh_irq_label label); int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, - int hw_irq, hh_irq_handle_fn on_release, void *data); + int hw_irq, hh_irq_handle_fn cb_handle, void *data); +int hh_irq_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + int hw_irq, hh_irq_handle_fn_v2 cb_handle, void *data); +int hh_irq_lend_notify(enum hh_irq_label label); int hh_irq_reclaim(enum hh_irq_label label); - int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, hh_irq_handle_fn on_lend, void *data); +int hh_irq_wait_for_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn_v2 on_lend, void *data); int hh_irq_accept(enum hh_irq_label label, int irq, int type); +int hh_irq_accept_notify(enum hh_irq_label label); int hh_irq_release(enum hh_irq_label label); +int hh_irq_release_notify(enum hh_irq_label label); #endif diff --git a/include/linux/haven/hh_rm_drv.h b/include/linux/haven/hh_rm_drv.h index b9beedc4f60c..710d33c2f6b9 100644 --- a/include/linux/haven/hh_rm_drv.h +++ b/include/linux/haven/hh_rm_drv.h @@ -141,6 +141,7 @@ struct hh_notify_vmid_desc { #define HH_RM_NOTIF_VM_STATUS 0x56100008 #define HH_RM_NOTIF_VM_IRQ_LENT 0x56100011 #define HH_RM_NOTIF_VM_IRQ_RELEASED 0x56100012 +#define HH_RM_NOTIF_VM_IRQ_ACCEPTED 0x56100013 #define HH_RM_VM_STATUS_NO_STATE 0 #define HH_RM_VM_STATUS_INIT 1 @@ -177,6 +178,10 @@ struct hh_rm_notif_vm_irq_released_payload { hh_virq_handle_t virq_handle; } __packed; +struct hh_rm_notif_vm_irq_accepted_payload { + hh_virq_handle_t virq_handle; +} __packed; + /* VM Services */ #define HH_RM_NOTIF_VM_CONSOLE_CHARS 0X56100080 @@ -197,10 +202,16 @@ int hh_rm_unregister_notifier(struct notifier_block *nb); int hh_rm_virq_to_irq(u32 virq, u32 type); int hh_rm_irq_to_virq(int irq, u32 *virq); +int hh_rm_vm_irq_lend(hh_vmid_t vmid, + int virq, + int label, + hh_virq_handle_t *virq_handle); +int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq); -int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle); +int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); +int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle); int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); + int hh_rm_vm_irq_reclaim(hh_virq_handle_t virq_handle); /* Client APIs for VM management */ @@ -260,23 +271,44 @@ static inline int hh_rm_virq_to_irq(u32 virq) return -EINVAL; } +static inline int hh_rm_vm_irq_lend(hh_vmid_t vmid, + int virq, + int label, + hh_virq_handle_t *virq_handle) +{ + return -EINVAL; +} + static inline int hh_rm_irq_to_virq(int irq, u32 *virq) { return -EINVAL; } +static inline int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, + hh_virq_handle_t virq_handle) +{ + return -EINVAL; +} + static inline int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) { return -EINVAL; + } -static inline int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label) +static inline int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, + hh_virq_handle_t virq_handle) +{ + return -EINVAL; +} + +static inline int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) { return -EINVAL; } static inline int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, - hh_virq_handle_t virq_handle) + hh_virq_handle_t virq_handle) { return -EINVAL; }