18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VMware VMCI Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 VMware, Inc. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_defs.h>
98c2ecf20Sopenharmony_ci#include <linux/vmw_vmci_api.h>
108c2ecf20Sopenharmony_ci#include <linux/list.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/sched.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/rculist.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "vmci_driver.h"
178c2ecf20Sopenharmony_ci#include "vmci_event.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define EVENT_MAGIC 0xEABE0000
208c2ecf20Sopenharmony_ci#define VMCI_EVENT_MAX_ATTEMPTS 10
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct vmci_subscription {
238c2ecf20Sopenharmony_ci	u32 id;
248c2ecf20Sopenharmony_ci	u32 event;
258c2ecf20Sopenharmony_ci	vmci_event_cb callback;
268c2ecf20Sopenharmony_ci	void *callback_data;
278c2ecf20Sopenharmony_ci	struct list_head node;	/* on one of subscriber lists */
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct list_head subscriber_array[VMCI_EVENT_MAX];
318c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(subscriber_mutex);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ciint __init vmci_event_init(void)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	int i;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	for (i = 0; i < VMCI_EVENT_MAX; i++)
388c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&subscriber_array[i]);
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	return VMCI_SUCCESS;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid vmci_event_exit(void)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	int e;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* We free all memory at exit. */
488c2ecf20Sopenharmony_ci	for (e = 0; e < VMCI_EVENT_MAX; e++) {
498c2ecf20Sopenharmony_ci		struct vmci_subscription *cur, *p2;
508c2ecf20Sopenharmony_ci		list_for_each_entry_safe(cur, p2, &subscriber_array[e], node) {
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci			/*
538c2ecf20Sopenharmony_ci			 * We should never get here because all events
548c2ecf20Sopenharmony_ci			 * should have been unregistered before we try
558c2ecf20Sopenharmony_ci			 * to unload the driver module.
568c2ecf20Sopenharmony_ci			 */
578c2ecf20Sopenharmony_ci			pr_warn("Unexpected free events occurring\n");
588c2ecf20Sopenharmony_ci			list_del(&cur->node);
598c2ecf20Sopenharmony_ci			kfree(cur);
608c2ecf20Sopenharmony_ci		}
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * Find entry. Assumes subscriber_mutex is held.
668c2ecf20Sopenharmony_ci */
678c2ecf20Sopenharmony_cistatic struct vmci_subscription *event_find(u32 sub_id)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	int e;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	for (e = 0; e < VMCI_EVENT_MAX; e++) {
728c2ecf20Sopenharmony_ci		struct vmci_subscription *cur;
738c2ecf20Sopenharmony_ci		list_for_each_entry(cur, &subscriber_array[e], node) {
748c2ecf20Sopenharmony_ci			if (cur->id == sub_id)
758c2ecf20Sopenharmony_ci				return cur;
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci	return NULL;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * Actually delivers the events to the subscribers.
838c2ecf20Sopenharmony_ci * The callback function for each subscriber is invoked.
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cistatic void event_deliver(struct vmci_event_msg *event_msg)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct vmci_subscription *cur;
888c2ecf20Sopenharmony_ci	struct list_head *subscriber_list;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	rcu_read_lock();
918c2ecf20Sopenharmony_ci	subscriber_list = &subscriber_array[event_msg->event_data.event];
928c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(cur, subscriber_list, node) {
938c2ecf20Sopenharmony_ci		cur->callback(cur->id, &event_msg->event_data,
948c2ecf20Sopenharmony_ci			      cur->callback_data);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	rcu_read_unlock();
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/*
1008c2ecf20Sopenharmony_ci * Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all
1018c2ecf20Sopenharmony_ci * subscribers for given event.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ciint vmci_event_dispatch(struct vmci_datagram *msg)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct vmci_event_msg *event_msg = (struct vmci_event_msg *)msg;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (msg->payload_size < sizeof(u32) ||
1088c2ecf20Sopenharmony_ci	    msg->payload_size > sizeof(struct vmci_event_data_max))
1098c2ecf20Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (!VMCI_EVENT_VALID(event_msg->event_data.event))
1128c2ecf20Sopenharmony_ci		return VMCI_ERROR_EVENT_UNKNOWN;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	event_deliver(event_msg);
1158c2ecf20Sopenharmony_ci	return VMCI_SUCCESS;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * vmci_event_subscribe() - Subscribe to a given event.
1208c2ecf20Sopenharmony_ci * @event:      The event to subscribe to.
1218c2ecf20Sopenharmony_ci * @callback:   The callback to invoke upon the event.
1228c2ecf20Sopenharmony_ci * @callback_data:      Data to pass to the callback.
1238c2ecf20Sopenharmony_ci * @subscription_id:    ID used to track subscription.  Used with
1248c2ecf20Sopenharmony_ci *              vmci_event_unsubscribe()
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Subscribes to the provided event. The callback specified will be
1278c2ecf20Sopenharmony_ci * fired from RCU critical section and therefore must not sleep.
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_ciint vmci_event_subscribe(u32 event,
1308c2ecf20Sopenharmony_ci			 vmci_event_cb callback,
1318c2ecf20Sopenharmony_ci			 void *callback_data,
1328c2ecf20Sopenharmony_ci			 u32 *new_subscription_id)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct vmci_subscription *sub;
1358c2ecf20Sopenharmony_ci	int attempts;
1368c2ecf20Sopenharmony_ci	int retval;
1378c2ecf20Sopenharmony_ci	bool have_new_id = false;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (!new_subscription_id) {
1408c2ecf20Sopenharmony_ci		pr_devel("%s: Invalid subscription (NULL)\n", __func__);
1418c2ecf20Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!VMCI_EVENT_VALID(event) || !callback) {
1458c2ecf20Sopenharmony_ci		pr_devel("%s: Failed to subscribe to event (type=%d) (callback=%p) (data=%p)\n",
1468c2ecf20Sopenharmony_ci			 __func__, event, callback, callback_data);
1478c2ecf20Sopenharmony_ci		return VMCI_ERROR_INVALID_ARGS;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	sub = kzalloc(sizeof(*sub), GFP_KERNEL);
1518c2ecf20Sopenharmony_ci	if (!sub)
1528c2ecf20Sopenharmony_ci		return VMCI_ERROR_NO_MEM;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	sub->id = VMCI_EVENT_MAX;
1558c2ecf20Sopenharmony_ci	sub->event = event;
1568c2ecf20Sopenharmony_ci	sub->callback = callback;
1578c2ecf20Sopenharmony_ci	sub->callback_data = callback_data;
1588c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sub->node);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	mutex_lock(&subscriber_mutex);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Creation of a new event is always allowed. */
1638c2ecf20Sopenharmony_ci	for (attempts = 0; attempts < VMCI_EVENT_MAX_ATTEMPTS; attempts++) {
1648c2ecf20Sopenharmony_ci		static u32 subscription_id;
1658c2ecf20Sopenharmony_ci		/*
1668c2ecf20Sopenharmony_ci		 * We try to get an id a couple of time before
1678c2ecf20Sopenharmony_ci		 * claiming we are out of resources.
1688c2ecf20Sopenharmony_ci		 */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/* Test for duplicate id. */
1718c2ecf20Sopenharmony_ci		if (!event_find(++subscription_id)) {
1728c2ecf20Sopenharmony_ci			sub->id = subscription_id;
1738c2ecf20Sopenharmony_ci			have_new_id = true;
1748c2ecf20Sopenharmony_ci			break;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (have_new_id) {
1798c2ecf20Sopenharmony_ci		list_add_rcu(&sub->node, &subscriber_array[event]);
1808c2ecf20Sopenharmony_ci		retval = VMCI_SUCCESS;
1818c2ecf20Sopenharmony_ci	} else {
1828c2ecf20Sopenharmony_ci		retval = VMCI_ERROR_NO_RESOURCES;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	mutex_unlock(&subscriber_mutex);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	*new_subscription_id = sub->id;
1888c2ecf20Sopenharmony_ci	return retval;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_event_subscribe);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/*
1938c2ecf20Sopenharmony_ci * vmci_event_unsubscribe() - unsubscribe from an event.
1948c2ecf20Sopenharmony_ci * @sub_id:     A subscription ID as provided by vmci_event_subscribe()
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Unsubscribe from given event. Removes it from list and frees it.
1978c2ecf20Sopenharmony_ci * Will return callback_data if requested by caller.
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_ciint vmci_event_unsubscribe(u32 sub_id)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct vmci_subscription *s;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	mutex_lock(&subscriber_mutex);
2048c2ecf20Sopenharmony_ci	s = event_find(sub_id);
2058c2ecf20Sopenharmony_ci	if (s)
2068c2ecf20Sopenharmony_ci		list_del_rcu(&s->node);
2078c2ecf20Sopenharmony_ci	mutex_unlock(&subscriber_mutex);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!s)
2108c2ecf20Sopenharmony_ci		return VMCI_ERROR_NOT_FOUND;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	synchronize_rcu();
2138c2ecf20Sopenharmony_ci	kfree(s);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return VMCI_SUCCESS;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmci_event_unsubscribe);
218