From f1b97c8d6d32fee5dddb0ae02799b1ef403a572e Mon Sep 17 00:00:00 2001 From: "Isaac J. Manjarres" Date: Wed, 15 Apr 2020 12:37:18 -0700 Subject: [PATCH] haven: Introduce a memory sharing notification framework Supplement the existing Haven memory resource notification interface with a framework that can be used to dispatch notifications about memory resources being shared/released to the resource owners. Change-Id: Ibbc14f4b29d68ffb1a9d6b035c79867783872225 Signed-off-by: Isaac J. Manjarres --- drivers/virt/haven/Kconfig | 12 ++ drivers/virt/haven/Makefile | 1 + drivers/virt/haven/hh_mem_notifier.c | 151 ++++++++++++++++++++++++++ include/linux/haven/hh_mem_notifier.h | 40 +++++++ 4 files changed, 204 insertions(+) create mode 100644 drivers/virt/haven/hh_mem_notifier.c create mode 100644 include/linux/haven/hh_mem_notifier.h diff --git a/drivers/virt/haven/Kconfig b/drivers/virt/haven/Kconfig index ce00fd916a17..e929eea35c01 100644 --- a/drivers/virt/haven/Kconfig +++ b/drivers/virt/haven/Kconfig @@ -56,4 +56,16 @@ config HH_IRQ_LEND supports sharing these interrupts. The follows RM recommended protocol. +config HH_MEM_NOTIFIER + tristate "Haven Memory Resource Notification Framework" + depends on HH_RM_DRV + help + The Haven Resource Manager allows for different memory resources + to be transferred across virtual machines with different notification + labels assigned to each resource to aid in distinguishing them. + Enabling the Haven Memory Resource Notification Framework provides an + interface for clients to transmit memory resources between virtual + machines, and register callbacks that get invoked only when + notifications pertaining to their memory resources arrive. + endif diff --git a/drivers/virt/haven/Makefile b/drivers/virt/haven/Makefile index 4aea5ea755e0..7307ae26fa79 100644 --- a/drivers/virt/haven/Makefile +++ b/drivers/virt/haven/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_HH_RM_DRV) += hh_rm_drv.o hh_rm_drv-y := hh_rm_core.o hh_rm_iface.o obj-$(CONFIG_HH_DBL) += hh_dbl.o obj-$(CONFIG_HH_IRQ_LEND) += hh_irq_lend.o +obj-$(CONFIG_HH_MEM_NOTIFIER) += hh_mem_notifier.o diff --git a/drivers/virt/haven/hh_mem_notifier.c b/drivers/virt/haven/hh_mem_notifier.c new file mode 100644 index 000000000000..4157240882e9 --- /dev/null +++ b/drivers/virt/haven/hh_mem_notifier.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include + +struct mem_notifier_entry { + hh_mem_notifier_handler handler; + void *data; +}; + +static DEFINE_MUTEX(mem_notifier_entries_lock); +static struct mem_notifier_entry mem_notifier_entries[HH_MEM_NOTIFIER_TAG_MAX]; + +static bool hh_mem_notifier_tag_valid(enum hh_mem_notifier_tag tag) +{ + return tag >= 0 && tag < HH_MEM_NOTIFIER_TAG_MAX; +} + +/** + * hh_mem_notifier_register: Bind a callback and arbitrary data to a particular + * notification tag. The callback will be invoked when + * the Haven MEM_SHARED and MEM_RELEASED notifications + * involving the tag that was registered with arrive + * at the VM. + * @tag: The tag for which the caller would like to receive MEM_SHARED and + * MEM_RELEASED notifications for + * @handler: The handler that will be invoked when a MEM_SHARED or MEM_RELEASED + * notification pertaining to @tag arrives at the VM. The handler will + * also be invoked with a pointer to caller specific data, as well as + * the original MEM_SHARED/MEM_RELEASED payload from the resource + * manager. + * @data: The data that should be passed to @handler when it is invoked + * + * On success, the function will return a cookie. Otherwise, the function will + * return an error, and thus, callers should use IS_ERR() to check for any + * errors. The cookie must be used when unregistering the handler from the + * tag. + */ +void *hh_mem_notifier_register(enum hh_mem_notifier_tag tag, + hh_mem_notifier_handler handler, void *data) +{ + struct mem_notifier_entry *entry; + + if (!hh_mem_notifier_tag_valid(tag) || !handler) + return ERR_PTR(-EINVAL); + + mutex_lock(&mem_notifier_entries_lock); + entry = &mem_notifier_entries[tag]; + if (entry->handler) { + mutex_unlock(&mem_notifier_entries_lock); + return ERR_PTR(-EEXIST); + } + entry->handler = handler; + entry->data = data; + mutex_unlock(&mem_notifier_entries_lock); + + return entry; +} +EXPORT_SYMBOL(hh_mem_notifier_register); + +/** + * hh_mem_notifier_unregister: Unregister for memory notifier notifications + * with respect to a particular tag. + * @cookie: The cookie returned by hh_mem_notifier_register + * + * On success, the function will unbind the handler specified in + * hh_mem_notifier_register from the tag, preventing the handler from being + * invoked when subsequent MEM_SHARED/MEM_RELEASED notifications pertaining + * to the tag arrive. + */ +void hh_mem_notifier_unregister(void *cookie) +{ + struct mem_notifier_entry *entry = cookie; + + if (!cookie) + return; + + mutex_lock(&mem_notifier_entries_lock); + entry->handler = NULL; + entry->data = NULL; + mutex_unlock(&mem_notifier_entries_lock); +} +EXPORT_SYMBOL(hh_mem_notifier_unregister); + +static enum hh_mem_notifier_tag hh_mem_notifier_get_tag(unsigned long action, + void *msg) +{ + return action == HH_RM_NOTIF_MEM_SHARED ? + ((struct hh_rm_notif_mem_shared_payload *)msg)->mem_info_tag : + ((struct hh_rm_notif_mem_released_payload *)msg)->mem_info_tag; +} + +static int hh_mem_notifier_call(struct notifier_block *nb, unsigned long action, + void *msg) +{ + struct mem_notifier_entry *entry; + enum hh_mem_notifier_tag tag; + hh_mem_notifier_handler handler = NULL; + void *data; + + if ((action != HH_RM_NOTIF_MEM_SHARED) && + (action != HH_RM_NOTIF_MEM_RELEASED)) + return NOTIFY_DONE; + + tag = hh_mem_notifier_get_tag(action, msg); + if (!hh_mem_notifier_tag_valid(tag)) + return NOTIFY_DONE; + + mutex_lock(&mem_notifier_entries_lock); + entry = &mem_notifier_entries[tag]; + handler = entry->handler; + data = entry->data; + mutex_unlock(&mem_notifier_entries_lock); + + if (handler) + handler(tag, action, data, msg); + + return NOTIFY_OK; +} + +static struct notifier_block hh_mem_notifier_blk = { + .notifier_call = hh_mem_notifier_call, +}; + +static int __init hh_mem_notifier_init(void) +{ + int ret = hh_rm_register_notifier(&hh_mem_notifier_blk); + + if (ret) + pr_err("%s: registration with RM notifier failed rc: %d\n", + __func__, ret); + return ret; +} +module_init(hh_mem_notifier_init); + +static void __exit hh_mem_notifier_exit(void) +{ + hh_rm_unregister_notifier(&hh_mem_notifier_blk); +} +module_exit(hh_mem_notifier_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Haven Memory Notifier"); diff --git a/include/linux/haven/hh_mem_notifier.h b/include/linux/haven/hh_mem_notifier.h new file mode 100644 index 000000000000..ed5a81355ae4 --- /dev/null +++ b/include/linux/haven/hh_mem_notifier.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __HH_MEM_NOTIFIER_H +#define __HH_MEM_NOTIFIER_H + +#include +#include +#include +#include + +enum hh_mem_notifier_tag { + HH_MEM_NOTIFIER_TAG_MAX +}; + +typedef void (*hh_mem_notifier_handler)(enum hh_mem_notifier_tag tag, + unsigned long notif_type, + void *entry_data, void *notif_msg); + +#if IS_ENABLED(CONFIG_HH_MEM_NOTIFIER) +void *hh_mem_notifier_register(enum hh_mem_notifier_tag tag, + hh_mem_notifier_handler notif_handler, + void *data); +void hh_mem_notifier_unregister(void *cookie); +#else +static void *hh_mem_notifier_register(enum hh_mem_notifier_tag tag, + hh_mem_notifier_handler notif_handler, + void *data) +{ + return ERR_PTR(-ENOTSUPP); +} + +static void hh_mem_notifier_unregister(void *cookie) +{ +} +#endif +#endif