From cfa1320d7f2f18068817b4b671c7ce29db736226 Mon Sep 17 00:00:00 2001 From: Murali Nalajala Date: Tue, 21 Apr 2020 22:30:42 -0700 Subject: [PATCH 1/5] soc: qcom: guestvm loader enhancements RM-VM(Resource Manager) send various vm status notifications(INIT/READY/ RUNNING etc) back to primary VM while PIL loader is loading the VM images. Listern to these notifications and make a call to get the hypervisor resources to configure doorbell and message queues. Change-Id: Ia378a1df82ef960e2c11bf715100e38de82b670c Signed-off-by: Murali Nalajala --- drivers/soc/qcom/guestvm_loader.c | 78 ++++++++++++++++++++++++------- drivers/virt/haven/hh_rm_core.c | 30 ++++-------- drivers/virt/haven/hh_rm_iface.c | 4 +- include/linux/haven/hh_rm_drv.h | 22 +++++++-- 4 files changed, 89 insertions(+), 45 deletions(-) diff --git a/drivers/soc/qcom/guestvm_loader.c b/drivers/soc/qcom/guestvm_loader.c index 201d0f33bec5..7eb0e0e10b47 100644 --- a/drivers/soc/qcom/guestvm_loader.c +++ b/drivers/soc/qcom/guestvm_loader.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -31,12 +30,13 @@ static struct kobj_type guestvm_kobj_type = { }; struct guestvm_loader_private { - struct work_struct vm_loader_work; + struct notifier_block guestvm_nb; struct kobject vm_loader_kobj; struct device *dev; char vm_name[MAX_LEN]; void *vm_loaded; int vmid; + u8 vm_status; }; static inline enum hh_vm_names get_hh_vm_name(const char *str) @@ -50,24 +50,50 @@ static inline enum hh_vm_names get_hh_vm_name(const char *str) return HH_VM_MAX; } -static void guestvm_loader_rm_notifier(struct work_struct *vm_loader_work) +static int guestvm_loader_nb_handler(struct notifier_block *this, + unsigned long cmd, void *data) { struct guestvm_loader_private *priv; - int ret = 0; + struct hh_rm_notif_vm_status_payload *vm_status_payload = data; + u8 vm_status = vm_status_payload->vm_status; + int ret; - priv = container_of(vm_loader_work, struct guestvm_loader_private, - vm_loader_work); - priv->vmid = hh_rm_vm_alloc_vmid(get_hh_vm_name(priv->vm_name)); - if (priv->vmid == HH_VM_MAX) { - dev_err(priv->dev, "Couldn't get vmid.\n"); - return; + priv = container_of(this, struct guestvm_loader_private, guestvm_nb); + + if (cmd != HH_RM_NOTIF_VM_STATUS) + return NOTIFY_DONE; + + if (priv->vmid != vm_status_payload->vmid) + dev_warn(priv->dev, "Expected a notification from vmid = %d, but received one from vmid = %d\n", + priv->vmid, vm_status_payload->vmid); + + /* + * Listen to STATUS_READY or STATUS_RUNNING notifications from RM. + * These notifications come from RM after PIL loading the VM images. + * Query GET_HYP_RESOURCES to populate other entities such as MessageQ + * and DBL. + */ + switch (vm_status) { + case HH_RM_VM_STATUS_READY: + priv->vm_status = HH_RM_VM_STATUS_READY; + ret = hh_rm_populate_hyp_res(vm_status_payload->vmid); + if (ret < 0) { + dev_err(priv->dev, "Failed to get hyp resources for vmid = %d ret = %d\n", + vm_status_payload->vmid, ret); + return NOTIFY_DONE; + } + break; + case HH_RM_VM_STATUS_RUNNING: + break; + default: + dev_err(priv->dev, "Unknown notification receieved for vmid = %d vm_status = %d\n", + vm_status_payload->vmid, vm_status); } - ret = hh_rm_vm_start(priv->vmid); - if (ret) - dev_err(priv->dev, "VM start has failed with %d.\n", ret); + + return NOTIFY_DONE; } -static ssize_t guestvm_load_start(struct kobject *kobj, +static ssize_t guestvm_loader_start(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) @@ -88,6 +114,13 @@ static ssize_t guestvm_load_start(struct kobject *kobj, } if (boot) { + priv->vm_status = HH_RM_VM_STATUS_INIT; + priv->vmid = hh_rm_vm_alloc_vmid(get_hh_vm_name(priv->vm_name)); + if (priv->vmid < 0) { + dev_err(priv->dev, "Couldn't allocate VMID.\n"); + return count; + } + priv->vm_loaded = subsystem_get(priv->vm_name); if (IS_ERR(priv->vm_loaded)) { ret = (int)(PTR_ERR(priv->vm_loaded)); @@ -96,13 +129,18 @@ static ssize_t guestvm_load_start(struct kobject *kobj, priv->vm_loaded = NULL; return ret; } - schedule_work(&priv->vm_loader_work); + + priv->vm_status = HH_RM_VM_STATUS_RUNNING; + ret = hh_rm_vm_start(priv->vmid); + if (ret) + dev_err(priv->dev, "VM start has failed for vmid = %d ret = %d\n", + priv->vmid, ret); } return count; } static struct kobj_attribute guestvm_loader_attribute = -__ATTR(boot_guestvm, 0220, NULL, guestvm_load_start); +__ATTR(boot_guestvm, 0220, NULL, guestvm_loader_start); static struct attribute *attrs[] = { &guestvm_loader_attribute.attr, @@ -132,8 +170,6 @@ static int guestvm_loader_probe(struct platform_device *pdev) return -EINVAL; strlcpy(priv->vm_name, sub_sys, sizeof(priv->vm_name)); - INIT_WORK(&priv->vm_loader_work, guestvm_loader_rm_notifier); - ret = kobject_init_and_add(&priv->vm_loader_kobj, &guestvm_kobj_type, kernel_kobj, "load_guestvm"); if (ret) { @@ -148,6 +184,12 @@ static int guestvm_loader_probe(struct platform_device *pdev) goto error_return; } + priv->guestvm_nb.notifier_call = guestvm_loader_nb_handler; + ret = hh_rm_register_notifier(&priv->guestvm_nb); + if (ret) + return ret; + + priv->vm_status = HH_RM_VM_STATUS_NO_STATE; return 0; error_return: diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index 501745b52c93..80252355bf8b 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "hh_rm_drv_private.h" @@ -66,8 +67,6 @@ SRCU_NOTIFIER_HEAD_STATIC(hh_rm_notifier); static void hh_rm_get_svm_res_work_fn(struct work_struct *work); static DECLARE_WORK(hh_rm_get_svm_res_work, hh_rm_get_svm_res_work_fn); -static int hh_rm_populate_hyp_res(hh_vmid_t vmid); - static struct hh_rm_connection *hh_rm_alloc_connection(u32 msg_id) { struct hh_rm_connection *connection; @@ -124,21 +123,6 @@ int hh_rm_unregister_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(hh_rm_unregister_notifier); -static int hh_rm_process_notif_vm_status(void *recv_buff, size_t recv_buff_size) -{ - struct hh_rm_notif_vm_status_payload *vm_status_payload; - - vm_status_payload = recv_buff + sizeof(struct hh_rm_rpc_hdr); - - /* The VM is now booting. Collect it's info and - * populate to other entities such as MessageQ and DBL - */ - if (vm_status_payload->vm_status == HH_RM_OS_STATUS_BOOT) - return hh_rm_populate_hyp_res(vm_status_payload->vmid); - - return 0; -} - static struct hh_rm_connection * hh_rm_wait_for_notif_fragments(void *recv_buff, size_t recv_buff_size) { @@ -214,8 +198,6 @@ static int hh_rm_process_notif(void *recv_buff, size_t recv_buff_size) ret = -EINVAL; goto err; } - - ret = hh_rm_process_notif_vm_status(recv_buff, recv_buff_size); break; case HH_RM_NOTIF_VM_IRQ_LENT: if (recv_buff_size != sizeof(*hdr) + @@ -698,7 +680,14 @@ err: return ret; } -static int hh_rm_populate_hyp_res(hh_vmid_t vmid) +/** + * hh_rm_populate_hyp_res: Query Resource Manager VM to get hyp resources. + * @vmid: The vmid of resources to be queried. + * + * 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_populate_hyp_res(hh_vmid_t vmid) { struct hh_vm_get_hyp_res_resp_entry *res_entries = NULL; int linux_irq, ret = 0; @@ -768,6 +757,7 @@ out: kfree(res_entries); return ret; } +EXPORT_SYMBOL(hh_rm_populate_hyp_res); static void hh_rm_get_svm_res_work_fn(struct work_struct *work) { diff --git a/drivers/virt/haven/hh_rm_iface.c b/drivers/virt/haven/hh_rm_iface.c index 5abfea564f50..bc95b508ee7c 100644 --- a/drivers/virt/haven/hh_rm_iface.c +++ b/drivers/virt/haven/hh_rm_iface.c @@ -506,13 +506,13 @@ int hh_rm_vm_start(int vmid) &resp_payload_size, &reply_err_code); if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { err = PTR_ERR(resp_payload); - pr_err("%s: VM_ALLOCATE failed with err: %d\n", + pr_err("%s: VM_START failed with err: %d\n", __func__, err); return err; } if (resp_payload_size != sizeof(*resp_payload)) { - pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n", + pr_err("%s: Invalid size received for VM_START: %u\n", __func__, resp_payload_size); kfree(resp_payload); return -EINVAL; diff --git a/include/linux/haven/hh_rm_drv.h b/include/linux/haven/hh_rm_drv.h index 37386ca41ed2..d8ac6e00e60e 100644 --- a/include/linux/haven/hh_rm_drv.h +++ b/include/linux/haven/hh_rm_drv.h @@ -130,11 +130,14 @@ struct hh_notify_vmid_desc { #define HH_RM_NOTIF_VM_IRQ_RELEASED 0x56100012 #define HH_RM_VM_STATUS_NO_STATE 0 -#define HH_RM_VM_STATUS_RUNNING 1 -#define HH_RM_VM_STATUS_PAUSED 2 -#define HH_RM_VM_STATUS_SHUTDOWN 3 -#define HH_RM_VM_STATUS_SHUTOFF 4 -#define HH_RM_VM_STATUS_CRASHED 5 +#define HH_RM_VM_STATUS_INIT 1 +#define HH_RM_VM_STATUS_READY 2 +#define HH_RM_VM_STATUS_RUNNING 3 +#define HH_RM_VM_STATUS_PAUSED 4 +#define HH_RM_VM_STATUS_SHUTDOWN 5 +#define HH_RM_VM_STATUS_SHUTOFF 6 +#define HH_RM_VM_STATUS_CRASHED 7 +#define HH_RM_VM_STATUS_INIT_FAILED 8 #define HH_RM_OS_STATUS_NONE 0 #define HH_RM_OS_STATUS_EARLY_BOOT 1 @@ -190,6 +193,9 @@ int hh_rm_get_vmid(enum hh_vm_names vm_name, hh_vmid_t *vmid); int hh_rm_get_vm_name(hh_vmid_t vmid, enum hh_vm_names *vm_name); int hh_rm_vm_start(int vmid); +/* Client APIs for VM query */ +int hh_rm_populate_hyp_res(hh_vmid_t vmid); + /* Client APIs for VM Services */ int hh_rm_console_open(hh_vmid_t vmid); int hh_rm_console_close(hh_vmid_t vmid); @@ -273,6 +279,12 @@ static inline int hh_rm_vm_start(int vmid) return -EINVAL; } +/* Client APIs for VM query */ +static inline int hh_rm_populate_hyp_res(hh_vmid_t vmid) +{ + return -EINVAL; +} + /* Client APIs for VM Services */ static inline int hh_rm_console_open(hh_vmid_t vmid) { From 90bc10978da1d22c3ee2326484c04d8643685900 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Thu, 23 Apr 2020 13:57:41 -0700 Subject: [PATCH 2/5] haven: dbl: Initialize cap ids to HH_CAPID_INVAL Currently, the cap-ids for doorbells are initialized to 0. However, 0 is a valid value for cap-id. Instead, initialize cap-ids to HH_CAPID_INVAL in the doorbell driver. Change-Id: Ia1588ed372f0657444e1ab39eab9e6c1e6d3d156 Signed-off-by: Elliot Berman --- drivers/virt/haven/hh_dbl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/virt/haven/hh_dbl.c b/drivers/virt/haven/hh_dbl.c index 945a09e8973e..d87f13da032d 100644 --- a/drivers/virt/haven/hh_dbl.c +++ b/drivers/virt/haven/hh_dbl.c @@ -67,7 +67,7 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, * There are no doorbell setup for Tx or Rx */ if (dir == HH_DBL_DIRECTION_RX) { - if (!cap_table_entry->rx_cap_id || + if ((cap_table_entry->rx_cap_id == HH_CAPID_INVAL) || !cap_table_entry->rx_reg_done) { pr_err("%s: label: %d; rx_cap_id: %llu; dir: %d rx_done: %d\n", __func__, client_desc->label, @@ -76,7 +76,7 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, return -EAGAIN; } } else { - if (!cap_table_entry->tx_cap_id || + if ((cap_table_entry->tx_cap_id == HH_CAPID_INVAL) || !cap_table_entry->tx_reg_done) { pr_err("%s: label: %d; tx_cap_id: %llu; dir: %d tx_done: %d\n", __func__, client_desc->label, @@ -575,6 +575,8 @@ static int __init hh_dbl_init(void) for (i = 0; i < HH_DBL_LABEL_MAX; i++) { entry = &hh_dbl_cap_table[i]; mutex_init(&entry->cap_entry_lock); + entry->tx_cap_id = HH_CAPID_INVAL; + entry->rx_cap_id = HH_CAPID_INVAL; entry->rx_irq_name = kasprintf(GFP_KERNEL, "hh_dbl_rx_%d", i); if (!entry->rx_irq_name) { ret = -ENOMEM; From d5e23615eaf96e5f2466a45ef852be25a8871f43 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Thu, 23 Apr 2020 14:01:39 -0700 Subject: [PATCH 3/5] haven: dbl: Support registration before dbl probe, resource population Currently, doorbell registration fails if a client driver attempts to probe before the doorbell probe since data structures are not initialized. Add an initialized flag which registration functions can check if data structures are initialized. Doorbell RX registration also fails if done before cap table is populated since RX IRQ is not yet known and RX registration attempts to register IRQ handler. IRQ registration should happen when populating the doorbell capabilities regardless of whether a client has registered. Change-Id: I448b291a5885683bee48b2eb34b51faba69a03bf Signed-off-by: Elliot Berman --- drivers/virt/haven/hh_dbl.c | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/virt/haven/hh_dbl.c b/drivers/virt/haven/hh_dbl.c index d87f13da032d..e1491b18e22b 100644 --- a/drivers/virt/haven/hh_dbl.c +++ b/drivers/virt/haven/hh_dbl.c @@ -36,6 +36,7 @@ struct hh_dbl_cap_table { void *rx_priv_data; }; +static bool hh_dbl_initialized; static struct hh_dbl_cap_table hh_dbl_cap_table[HH_DBL_LABEL_MAX]; /** @@ -278,6 +279,9 @@ void *hh_dbl_tx_register(enum hh_dbl_label label) if (label < 0 || label >= HH_DBL_LABEL_MAX) return ERR_PTR(-EINVAL); + if (!hh_dbl_initialized) + return ERR_PTR(-EPROBE_DEFER); + cap_table_entry = &hh_dbl_cap_table[label]; if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) @@ -338,6 +342,9 @@ void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb, void *priv) if (label < 0 || label >= HH_DBL_LABEL_MAX) return ERR_PTR(-EINVAL); + if (!hh_dbl_initialized) + return ERR_PTR(-EPROBE_DEFER); + cap_table_entry = &hh_dbl_cap_table[label]; if (mutex_lock_interruptible(&cap_table_entry->cap_entry_lock)) @@ -365,20 +372,6 @@ void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb, void *priv) cap_table_entry->rx_callback = rx_cb; cap_table_entry->rx_priv_data = priv; - ret = request_threaded_irq(cap_table_entry->rx_irq, - NULL, - hh_dbl_rx_callback_thread, - IRQF_ONESHOT, - cap_table_entry->rx_irq_name, - cap_table_entry); - - if (ret < 0) { - pr_err("%s: IRQ registration failed\n", __func__); - cap_table_entry->rx_callback = NULL; - cap_table_entry->rx_priv_data = NULL; - goto err; - } - cap_table_entry->rx_reg_done = 1; pr_debug("%s: Registered Rx client for label: %d\n", __func__, label); @@ -407,7 +400,7 @@ int hh_dbl_tx_unregister(void *dbl_client_desc) struct hh_dbl_desc *client_desc = dbl_client_desc; struct hh_dbl_cap_table *cap_table_entry; - if (!client_desc) + if (IS_ERR_OR_NULL(client_desc)) return -EINVAL; /* Check if the client has manipulated the label */ @@ -458,7 +451,7 @@ int hh_dbl_rx_unregister(void *dbl_client_desc) struct hh_dbl_desc *client_desc = dbl_client_desc; struct hh_dbl_cap_table *cap_table_entry; - if (!client_desc) + if (IS_ERR_OR_NULL(client_desc)) return -EINVAL; /* Check if the client has manipulated the label */ @@ -537,6 +530,19 @@ int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id, } cap_table_entry->rx_cap_id = cap_id; cap_table_entry->rx_irq = rx_irq; + + ret = request_threaded_irq(cap_table_entry->rx_irq, + NULL, + hh_dbl_rx_callback_thread, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + cap_table_entry->rx_irq_name, + cap_table_entry); + + if (ret < 0) { + pr_err("%s: IRQ registration failed\n", __func__); + goto err; + } + pr_debug("%s: label: %d; rx_cap_id: %llu; dir: %d; rx_irq: %d\n", __func__, label, cap_id, direction, rx_irq); break; @@ -584,6 +590,8 @@ static int __init hh_dbl_init(void) } } + hh_dbl_initialized = true; + return 0; err: From 90f8f682edc2b2ad1ae764b7cd312a3abf00a015 Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Fri, 24 Apr 2020 12:33:28 -0700 Subject: [PATCH 4/5] neuron: ch_haven: Move msgq init to sync thread The neuron channels may init and probe before the secondary vm is loaded and the resources queried from resource manager. This results in either a blocking call or EAGAIN returned when sending a doorbell. Move the msgq_init call from the probe to the channel sync thread. This will ensure the init thread can continue if the doorbell send api needs to block or busywait loop. Change-Id: I0f5562968d59a3dc0a361aca9e4e60ffebcb70be Signed-off-by: Chris Lew --- net/neuron/channel/ch_haven_recv.c | 12 +++++------- net/neuron/channel/ch_haven_send.c | 28 ++++++++++++++-------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/net/neuron/channel/ch_haven_recv.c b/net/neuron/channel/ch_haven_recv.c index ad654c489239..5f9375c857a7 100644 --- a/net/neuron/channel/ch_haven_recv.c +++ b/net/neuron/channel/ch_haven_recv.c @@ -251,6 +251,11 @@ static int channel_sync_thread(void *data) struct neuron_shmem_channel_header *hdr; struct neuron_mq_data_priv *priv = (struct neuron_mq_data_priv *)data; + /* Init the shared memory header and local message queue. */ + if (msgq_init(priv)) { + pr_err("%s: msgq_init failed\n", __func__); + return 0; + } hdr = (struct neuron_shmem_channel_header *)priv->base; /* Waiting for head being updated by the sender. */ @@ -380,12 +385,7 @@ static int channel_hh_probe(struct neuron_channel *cdev) goto fail_rx_dbl; } - /* Init the shared memory header and local message queue. */ - ret = msgq_init(priv); - if (ret) - goto fail_mask; init_waitqueue_head(&priv->wait_q); - /* Start the thread for syncing with the sender. */ priv->sync_thread = kthread_run(channel_sync_thread, priv, "recv_sync_thread"); @@ -394,8 +394,6 @@ static int channel_hh_probe(struct neuron_channel *cdev) return 0; -fail_mask: - hh_dbl_rx_unregister(priv->rx_dbl); fail_rx_dbl: hh_dbl_tx_unregister(priv->tx_dbl); fail_tx_dbl: diff --git a/net/neuron/channel/ch_haven_send.c b/net/neuron/channel/ch_haven_send.c index f803d7a04485..7a4f5d322615 100644 --- a/net/neuron/channel/ch_haven_send.c +++ b/net/neuron/channel/ch_haven_send.c @@ -217,6 +217,19 @@ static int read_config(struct neuron_mq_data_priv *priv) return 0; } +static void msgq_init(struct neuron_mq_data_priv *priv) +{ + struct neuron_shmem_channel_header *hdr = priv->base; + struct neuron_msg_queue *msgq = &priv->msgq; + + msgq->headp = &hdr->head; + msgq->space_for_next_p = &hdr->space_for_next; + msgq->offset = (u32)-1; + + /* Set it to -1 as UNINITIALIZED */ + smp_store_release(msgq->headp, (u32)-1); +} + /* Thread to sync with the receiver. Note: this thread might never finishes if * it fails to sync with the peer. */ @@ -225,6 +238,7 @@ static int channel_sync_thread(void *data) struct neuron_mq_data_priv *priv = (struct neuron_mq_data_priv *)data; struct neuron_shmem_channel_header *hdr; + msgq_init(priv); hdr = (struct neuron_shmem_channel_header *)priv->base; hdr->version = CHANNEL_VERSION; hdr->tail_offset = -1; @@ -265,19 +279,6 @@ static int channel_sync_thread(void *data) return 0; } -static void msgq_init(struct neuron_mq_data_priv *priv) -{ - struct neuron_shmem_channel_header *hdr = priv->base; - struct neuron_msg_queue *msgq = &priv->msgq; - - msgq->headp = &hdr->head; - msgq->space_for_next_p = &hdr->space_for_next; - msgq->offset = (u32)-1; - - /* Set it to -1 as UNINITIALIZED */ - smp_store_release(msgq->headp, (u32)-1); -} - static int channel_hh_map_memory(struct neuron_mq_data_priv *priv, struct device *dev) { @@ -367,7 +368,6 @@ static int channel_hh_probe(struct neuron_channel *cdev) goto fail_rx_dbl; } - msgq_init(priv); init_waitqueue_head(&priv->wait_q); /* Start the thread for syncing with the receiver. */ priv->sync_thread = kthread_run(channel_sync_thread, priv, From 23f7911bfe3f8752641fba870027a681d5b28ce9 Mon Sep 17 00:00:00 2001 From: Elliot Berman Date: Thu, 23 Apr 2020 23:23:59 -0700 Subject: [PATCH 5/5] haven: dbl: Make hh_dbl_ functions wait for capid availability Client drivers may successfully register with a doorbell even before doorbell capids are known to Linux. However, we do need to know capids for send, read, reset, and mask functions. Here, we mirror the behavior of message queues by placing the calling thread in a waitqueue until the capids are populated by RM. Change-Id: Ie1cf98d0fe344bb2a878bb655db78705d4292ff9 Signed-off-by: Elliot Berman --- drivers/virt/haven/hh_dbl.c | 77 ++++++++++++++++++++---------- include/linux/haven/hh_dbl.h | 24 ++++++---- net/neuron/channel/ch_haven_recv.c | 2 +- net/neuron/channel/ch_haven_send.c | 2 +- net/qrtr/haven.c | 2 +- 5 files changed, 72 insertions(+), 35 deletions(-) diff --git a/drivers/virt/haven/hh_dbl.c b/drivers/virt/haven/hh_dbl.c index e1491b18e22b..d08a27340482 100644 --- a/drivers/virt/haven/hh_dbl.c +++ b/drivers/virt/haven/hh_dbl.c @@ -34,6 +34,7 @@ struct hh_dbl_cap_table { const char *rx_irq_name; dbl_rx_cb_t rx_callback; void *rx_priv_data; + wait_queue_head_t cap_wq; }; static bool hh_dbl_initialized; @@ -43,11 +44,11 @@ static struct hh_dbl_cap_table hh_dbl_cap_table[HH_DBL_LABEL_MAX]; * hh_dbl_validate_params - Validate doorbell common parameters */ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, - enum hh_dbl_dir dir) + enum hh_dbl_dir dir, const unsigned long flags) { struct hh_dbl_cap_table *cap_table_entry; - if (!client_desc) + if (IS_ERR_OR_NULL(client_desc)) return -EINVAL; /* Check if the client has manipulated the label */ @@ -68,23 +69,35 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, * There are no doorbell setup for Tx or Rx */ if (dir == HH_DBL_DIRECTION_RX) { - if ((cap_table_entry->rx_cap_id == HH_CAPID_INVAL) || - !cap_table_entry->rx_reg_done) { - pr_err("%s: label: %d; rx_cap_id: %llu; dir: %d rx_done: %d\n", - __func__, client_desc->label, - cap_table_entry->rx_cap_id, - dir, cap_table_entry->rx_reg_done); + if (!cap_table_entry->rx_reg_done) + return -EINVAL; + + if (cap_table_entry->rx_cap_id != HH_CAPID_INVAL) + return 0; + + if (flags & HH_DBL_NONBLOCK) return -EAGAIN; - } + + if (wait_event_interruptible(cap_table_entry->cap_wq, + cap_table_entry->rx_cap_id != HH_CAPID_INVAL)) + return -ERESTARTSYS; + + return 0; } else { - if ((cap_table_entry->tx_cap_id == HH_CAPID_INVAL) || - !cap_table_entry->tx_reg_done) { - pr_err("%s: label: %d; tx_cap_id: %llu; dir: %d tx_done: %d\n", - __func__, client_desc->label, - cap_table_entry->tx_cap_id, - dir, cap_table_entry->tx_reg_done); + if (!cap_table_entry->tx_reg_done) + return -EINVAL; + + if (cap_table_entry->tx_cap_id != HH_CAPID_INVAL) + return 0; + + if (flags & HH_DBL_NONBLOCK) return -EAGAIN; - } + + if (wait_event_interruptible(cap_table_entry->cap_wq, + cap_table_entry->tx_cap_id != HH_CAPID_INVAL)) + return -ERESTARTSYS; + + return 0; } return 0; @@ -94,6 +107,8 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, * hh_dbl_read_and_clean - Automatically read and clear the flags in doorbell * @client_desc: client handle to indetify the doorbell object * @clear_flags: clear the bits mentioned in the clear_flags + * @flags: Optional flags to pass to send the data. For the list of flags, + * see linux/haven/hh_dbl.h * * Reads and clears the flags of the Doorbell object. If there is a pending * bound virtual interrupt, it will be de-asserted @@ -102,7 +117,8 @@ static int hh_dbl_validate_params(struct hh_dbl_desc *client_desc, * 0 on success, @clear_flags contains the doorbell’s previous unmasked flags * before the @clear_flags were removed. */ -int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags) +int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags, + const unsigned long flags) { struct hh_dbl_cap_table *cap_table_entry; struct hh_hcall_dbl_recv_resp recv_resp; @@ -112,7 +128,7 @@ int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags) if (!clear_flags) return -EINVAL; - ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX); + ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags); if (ret) return ret; @@ -138,6 +154,8 @@ EXPORT_SYMBOL(hh_dbl_read_and_clean); * the doorbell's bound virtual interrupt * @ack_mask: Controls which flags should be automatically cleared * when the interrupt is asserted + * @flags: Optional flags to pass to send the data. For the list of flags, + * see linux/haven/hh_dbl.h * * Sets the Doorbell object’s masks. A doorbell object has two masks * which are configured by the receiver to control which flags it is @@ -147,13 +165,13 @@ EXPORT_SYMBOL(hh_dbl_read_and_clean); * 0 on success */ int hh_dbl_set_mask(void *dbl_client_desc, hh_dbl_flags_t enable_mask, - hh_dbl_flags_t ack_mask) + hh_dbl_flags_t ack_mask, const unsigned long flags) { struct hh_dbl_cap_table *cap_table_entry; struct hh_dbl_desc *client_desc = dbl_client_desc; int ret, hh_ret; - ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX); + ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags); if (ret) return ret; @@ -177,6 +195,8 @@ EXPORT_SYMBOL(hh_dbl_set_mask); * @client_desc: client handle to indetify the doorbell object * @newflags: flags to set in the doorbell. This flag along with enable_mask * in the doorbell decide whehter to raise vIRQ are not. + * @flags: Optional flags to pass to send the data. For the list of flags, + * see linux/haven/hh_dbl.h * * Set flags in the doorbell. If following the send, the set of enabled flags * as defined by the bitwise-AND of the doorbell flags with the EnableMask, @@ -186,7 +206,8 @@ EXPORT_SYMBOL(hh_dbl_set_mask); * 0 on success, @newflags contains the doorbell’s previous unmasked flags * before the @newflags were added. */ -int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags) +int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags, + unsigned long flags) { struct hh_dbl_cap_table *cap_table_entry; struct hh_hcall_dbl_send_resp send_resp; @@ -196,7 +217,7 @@ int hh_dbl_send(void *dbl_client_desc, hh_dbl_flags_t *newflags) if (!newflags) return -EINVAL; - ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_TX); + ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_TX, flags); if (ret) return ret; @@ -219,6 +240,8 @@ EXPORT_SYMBOL(hh_dbl_send); * hh_dbl_reset - clear all the flags of the doorbell and sets all bits in * the Doorbell’s mask. * @client_desc: client handle to indetify the doorbell object + * @flags: Optional flags to pass to send the data. For the list of flags, + * see linux/haven/hh_dbl.h * * Clears all the flags of the doorbell and sets all bits in the doorbell’s * mask. If there is a pending bound virtual interrupt, it will be de-asserted. @@ -226,13 +249,13 @@ EXPORT_SYMBOL(hh_dbl_send); * Returns: * 0 on success */ -int hh_dbl_reset(void *dbl_client_desc) +int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags) { struct hh_dbl_cap_table *cap_table_entry; struct hh_dbl_desc *client_desc = dbl_client_desc; int ret, hh_ret; - ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX); + ret = hh_dbl_validate_params(client_desc, HH_DBL_DIRECTION_RX, flags); if (ret) return ret; @@ -518,6 +541,9 @@ int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id, goto err; } cap_table_entry->tx_cap_id = cap_id; + + wake_up_interruptible(&cap_table_entry->cap_wq); + pr_debug("%s: label: %d; tx_cap_id: %llu; dir: %d; rx_irq: %d\n", __func__, label, cap_id, direction, rx_irq); break; @@ -543,6 +569,8 @@ int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id, goto err; } + wake_up_interruptible(&cap_table_entry->cap_wq); + pr_debug("%s: label: %d; rx_cap_id: %llu; dir: %d; rx_irq: %d\n", __func__, label, cap_id, direction, rx_irq); break; @@ -581,6 +609,7 @@ static int __init hh_dbl_init(void) for (i = 0; i < HH_DBL_LABEL_MAX; i++) { entry = &hh_dbl_cap_table[i]; mutex_init(&entry->cap_entry_lock); + init_waitqueue_head(&entry->cap_wq); entry->tx_cap_id = HH_CAPID_INVAL; entry->rx_cap_id = HH_CAPID_INVAL; entry->rx_irq_name = kasprintf(GFP_KERNEL, "hh_dbl_rx_%d", i); diff --git a/include/linux/haven/hh_dbl.h b/include/linux/haven/hh_dbl.h index 3c092bdea129..ab5c4ca40536 100644 --- a/include/linux/haven/hh_dbl.h +++ b/include/linux/haven/hh_dbl.h @@ -19,6 +19,9 @@ enum hh_dbl_label { HH_DBL_LABEL_MAX }; +/* Possible flags to pass for send, set_mask, read, reset */ +#define HH_DBL_NONBLOCK BIT(32) + #if IS_ENABLED(CONFIG_HH_DBL) void *hh_dbl_tx_register(enum hh_dbl_label label); void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb, @@ -27,11 +30,13 @@ void *hh_dbl_rx_register(enum hh_dbl_label label, dbl_rx_cb_t rx_cb, int hh_dbl_tx_unregister(void *dbl_client_desc); int hh_dbl_rx_unregister(void *dbl_client_desc); -int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags); +int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags, + const unsigned long flags); int hh_dbl_set_mask(void *dbl_client_desc, hh_dbl_flags_t enable_mask, - hh_dbl_flags_t ack_mask); -int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags); -int hh_dbl_reset(void *dbl_client_desc); + hh_dbl_flags_t ack_mask, const unsigned long flags); +int hh_dbl_read_and_clean(void *dbl_client_desc, hh_dbl_flags_t *clear_flags, + const unsigned long flags); +int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags); int hh_dbl_populate_cap_info(enum hh_dbl_label label, u64 cap_id, int direction, int rx_irq); #else @@ -57,25 +62,28 @@ static inline int hh_dbl_rx_unregister(void *dbl_client_desc) return ERR_PTR(-ENODEV); } -static inline int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags) +static inline int hh_dbl_send(void *dbl_client_desc, uint64_t *newflags, + const unsigned long flags) { return -EINVAL; } static inline int hh_dbl_set_mask(void *dbl_client_desc, hh_dbl_flags_t enable_mask, - hh_dbl_flags_t ack_mask) + hh_dbl_flags_t ack_mask, + const unsigned long flags) { return -EINVAL; } static inline int hh_dbl_read_and_clean(void *dbl_client_desc, - hh_dbl_flags_t *clear_flags) + hh_dbl_flags_t *clear_flags, + const unsigned long flags) { return -EINVAL; } -static inline int hh_dbl_reset(void *dbl_client_desc) +static inline int hh_dbl_reset(void *dbl_client_desc, const unsigned long flags) { return -EINVAL; } diff --git a/net/neuron/channel/ch_haven_recv.c b/net/neuron/channel/ch_haven_recv.c index 5f9375c857a7..47f0a6568436 100644 --- a/net/neuron/channel/ch_haven_recv.c +++ b/net/neuron/channel/ch_haven_recv.c @@ -94,7 +94,7 @@ static inline int channel_hh_kick(struct neuron_mq_data_priv *priv) hh_dbl_flags_t dbl_mask = CH_DBL_MASK; int ret; - ret = hh_dbl_send(priv->tx_dbl, &dbl_mask); + ret = hh_dbl_send(priv->tx_dbl, &dbl_mask, 0); if (ret) pr_err("failed to raise virq to the sender %d\n", ret); diff --git a/net/neuron/channel/ch_haven_send.c b/net/neuron/channel/ch_haven_send.c index 7a4f5d322615..43ceb3387c16 100644 --- a/net/neuron/channel/ch_haven_send.c +++ b/net/neuron/channel/ch_haven_send.c @@ -68,7 +68,7 @@ static inline int channel_hh_kick(struct neuron_mq_data_priv *priv) hh_dbl_flags_t dbl_mask = CH_DBL_MASK; int ret; - ret = hh_dbl_send(priv->tx_dbl, &dbl_mask); + ret = hh_dbl_send(priv->tx_dbl, &dbl_mask, 0); if (ret) pr_err("failed to raise virq to the sender %d\n", ret); diff --git a/net/qrtr/haven.c b/net/qrtr/haven.c index 446d42dd85ec..fc39f96c6b01 100644 --- a/net/qrtr/haven.c +++ b/net/qrtr/haven.c @@ -68,7 +68,7 @@ static void qrtr_haven_kick(struct qrtr_haven_dev *qdev) hh_dbl_flags_t dbl_mask = QRTR_DBL_MASK; int ret; - ret = hh_dbl_send(qdev->tx_dbl, &dbl_mask); + ret = hh_dbl_send(qdev->tx_dbl, &dbl_mask, HH_DBL_NONBLOCK); if (ret) dev_err(qdev->dev, "failed to raise doorbell %d\n", ret); }