Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1b97c8d authored by Isaac J. Manjarres's avatar Isaac J. Manjarres
Browse files

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: default avatarIsaac J. Manjarres <isaacm@codeaurora.org>
parent 01497f5e
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+151 −0
Original line number Diff line number Diff line
// 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");
+40 −0
Original line number Diff line number Diff line
/* 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