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