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 <isaacm@codeaurora.org>
This commit is contained in:
Isaac J. Manjarres 2020-04-15 12:37:18 -07:00
commit f1b97c8d6d
4 changed files with 204 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/haven/hh_mem_notifier.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/slab.h>
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");

View file

@ -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 <linux/err.h>
#include <linux/errno.h>
#include <linux/haven/hh_rm_drv.h>
#include <linux/types.h>
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