Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

VMCI: event handling implementation.

VMCI event code that manages event handlers and handles callbacks when specific
events fire.

Signed-off-by: George Zhang <georgezhang@vmware.com>
Acked-by: Andy king <acking@vmware.com>
Acked-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

George Zhang and committed by
Greg Kroah-Hartman
1d990201 197dbaaa

+249
+224
drivers/misc/vmw_vmci/vmci_event.c
··· 1 + /* 2 + * VMware VMCI Driver 3 + * 4 + * Copyright (C) 2012 VMware, Inc. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License as published by the 8 + * Free Software Foundation version 2 and no later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, but 11 + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 + * for more details. 14 + */ 15 + 16 + #include <linux/vmw_vmci_defs.h> 17 + #include <linux/vmw_vmci_api.h> 18 + #include <linux/list.h> 19 + #include <linux/module.h> 20 + #include <linux/sched.h> 21 + #include <linux/slab.h> 22 + 23 + #include "vmci_driver.h" 24 + #include "vmci_event.h" 25 + 26 + #define EVENT_MAGIC 0xEABE0000 27 + #define VMCI_EVENT_MAX_ATTEMPTS 10 28 + 29 + struct vmci_subscription { 30 + u32 id; 31 + u32 event; 32 + vmci_event_cb callback; 33 + void *callback_data; 34 + struct list_head node; /* on one of subscriber lists */ 35 + }; 36 + 37 + static struct list_head subscriber_array[VMCI_EVENT_MAX]; 38 + static DEFINE_MUTEX(subscriber_mutex); 39 + 40 + int __init vmci_event_init(void) 41 + { 42 + int i; 43 + 44 + for (i = 0; i < VMCI_EVENT_MAX; i++) 45 + INIT_LIST_HEAD(&subscriber_array[i]); 46 + 47 + return VMCI_SUCCESS; 48 + } 49 + 50 + void vmci_event_exit(void) 51 + { 52 + int e; 53 + 54 + /* We free all memory at exit. */ 55 + for (e = 0; e < VMCI_EVENT_MAX; e++) { 56 + struct vmci_subscription *cur, *p2; 57 + list_for_each_entry_safe(cur, p2, &subscriber_array[e], node) { 58 + 59 + /* 60 + * We should never get here because all events 61 + * should have been unregistered before we try 62 + * to unload the driver module. 63 + */ 64 + pr_warn("Unexpected free events occurring\n"); 65 + list_del(&cur->node); 66 + kfree(cur); 67 + } 68 + } 69 + } 70 + 71 + /* 72 + * Find entry. Assumes subscriber_mutex is held. 73 + */ 74 + static struct vmci_subscription *event_find(u32 sub_id) 75 + { 76 + int e; 77 + 78 + for (e = 0; e < VMCI_EVENT_MAX; e++) { 79 + struct vmci_subscription *cur; 80 + list_for_each_entry(cur, &subscriber_array[e], node) { 81 + if (cur->id == sub_id) 82 + return cur; 83 + } 84 + } 85 + return NULL; 86 + } 87 + 88 + /* 89 + * Actually delivers the events to the subscribers. 90 + * The callback function for each subscriber is invoked. 91 + */ 92 + static void event_deliver(struct vmci_event_msg *event_msg) 93 + { 94 + struct vmci_subscription *cur; 95 + struct list_head *subscriber_list; 96 + 97 + rcu_read_lock(); 98 + subscriber_list = &subscriber_array[event_msg->event_data.event]; 99 + list_for_each_entry_rcu(cur, subscriber_list, node) { 100 + cur->callback(cur->id, &event_msg->event_data, 101 + cur->callback_data); 102 + } 103 + rcu_read_unlock(); 104 + } 105 + 106 + /* 107 + * Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all 108 + * subscribers for given event. 109 + */ 110 + int vmci_event_dispatch(struct vmci_datagram *msg) 111 + { 112 + struct vmci_event_msg *event_msg = (struct vmci_event_msg *)msg; 113 + 114 + if (msg->payload_size < sizeof(u32) || 115 + msg->payload_size > sizeof(struct vmci_event_data_max)) 116 + return VMCI_ERROR_INVALID_ARGS; 117 + 118 + if (!VMCI_EVENT_VALID(event_msg->event_data.event)) 119 + return VMCI_ERROR_EVENT_UNKNOWN; 120 + 121 + event_deliver(event_msg); 122 + return VMCI_SUCCESS; 123 + } 124 + 125 + /* 126 + * vmci_event_subscribe() - Subscribe to a given event. 127 + * @event: The event to subscribe to. 128 + * @callback: The callback to invoke upon the event. 129 + * @callback_data: Data to pass to the callback. 130 + * @subscription_id: ID used to track subscription. Used with 131 + * vmci_event_unsubscribe() 132 + * 133 + * Subscribes to the provided event. The callback specified will be 134 + * fired from RCU critical section and therefore must not sleep. 135 + */ 136 + int vmci_event_subscribe(u32 event, 137 + vmci_event_cb callback, 138 + void *callback_data, 139 + u32 *new_subscription_id) 140 + { 141 + struct vmci_subscription *sub; 142 + int attempts; 143 + int retval; 144 + bool have_new_id = false; 145 + 146 + if (!new_subscription_id) { 147 + pr_devel("%s: Invalid subscription (NULL)\n", __func__); 148 + return VMCI_ERROR_INVALID_ARGS; 149 + } 150 + 151 + if (!VMCI_EVENT_VALID(event) || !callback) { 152 + pr_devel("%s: Failed to subscribe to event (type=%d) (callback=%p) (data=%p)\n", 153 + __func__, event, callback, callback_data); 154 + return VMCI_ERROR_INVALID_ARGS; 155 + } 156 + 157 + sub = kzalloc(sizeof(*sub), GFP_KERNEL); 158 + if (!sub) 159 + return VMCI_ERROR_NO_MEM; 160 + 161 + sub->id = VMCI_EVENT_MAX; 162 + sub->event = event; 163 + sub->callback = callback; 164 + sub->callback_data = callback_data; 165 + INIT_LIST_HEAD(&sub->node); 166 + 167 + mutex_lock(&subscriber_mutex); 168 + 169 + /* Creation of a new event is always allowed. */ 170 + for (attempts = 0; attempts < VMCI_EVENT_MAX_ATTEMPTS; attempts++) { 171 + static u32 subscription_id; 172 + /* 173 + * We try to get an id a couple of time before 174 + * claiming we are out of resources. 175 + */ 176 + 177 + /* Test for duplicate id. */ 178 + if (!event_find(++subscription_id)) { 179 + sub->id = subscription_id; 180 + have_new_id = true; 181 + break; 182 + } 183 + } 184 + 185 + if (have_new_id) { 186 + list_add_rcu(&sub->node, &subscriber_array[event]); 187 + retval = VMCI_SUCCESS; 188 + } else { 189 + retval = VMCI_ERROR_NO_RESOURCES; 190 + } 191 + 192 + mutex_unlock(&subscriber_mutex); 193 + 194 + *new_subscription_id = sub->id; 195 + return retval; 196 + } 197 + EXPORT_SYMBOL_GPL(vmci_event_subscribe); 198 + 199 + /* 200 + * vmci_event_unsubscribe() - unsubscribe from an event. 201 + * @sub_id: A subscription ID as provided by vmci_event_subscribe() 202 + * 203 + * Unsubscribe from given event. Removes it from list and frees it. 204 + * Will return callback_data if requested by caller. 205 + */ 206 + int vmci_event_unsubscribe(u32 sub_id) 207 + { 208 + struct vmci_subscription *s; 209 + 210 + mutex_lock(&subscriber_mutex); 211 + s = event_find(sub_id); 212 + if (s) 213 + list_del_rcu(&s->node); 214 + mutex_unlock(&subscriber_mutex); 215 + 216 + if (!s) 217 + return VMCI_ERROR_NOT_FOUND; 218 + 219 + synchronize_rcu(); 220 + kfree(s); 221 + 222 + return VMCI_SUCCESS; 223 + } 224 + EXPORT_SYMBOL_GPL(vmci_event_unsubscribe);
+25
drivers/misc/vmw_vmci/vmci_event.h
··· 1 + /* 2 + * VMware VMCI Driver 3 + * 4 + * Copyright (C) 2012 VMware, Inc. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License as published by the 8 + * Free Software Foundation version 2 and no later version. 9 + * 10 + * This program is distributed in the hope that it will be useful, but 11 + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 12 + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 + * for more details. 14 + */ 15 + 16 + #ifndef __VMCI_EVENT_H__ 17 + #define __VMCI_EVENT_H__ 18 + 19 + #include <linux/vmw_vmci_api.h> 20 + 21 + int vmci_event_init(void); 22 + void vmci_event_exit(void); 23 + int vmci_event_dispatch(struct vmci_datagram *msg); 24 + 25 + #endif /*__VMCI_EVENT_H__ */