1 /*-
2 * Copyright (c) 2018 VMware, Inc.
3 *
4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5 */
6
7 /* This file implements VMCI Event code. */
8
9 #include <sys/cdefs.h>
10 __FBSDID("$FreeBSD$");
11
12 #include "vmci.h"
13 #include "vmci_driver.h"
14 #include "vmci_event.h"
15 #include "vmci_kernel_api.h"
16 #include "vmci_kernel_defs.h"
17 #include "vmci_kernel_if.h"
18
19 #define LGPFX "vmci_event: "
20 #define EVENT_MAGIC 0xEABE0000
21
22 struct vmci_subscription {
23 vmci_id id;
24 int ref_count;
25 bool run_delayed;
26 vmci_event destroy_event;
27 vmci_event_type event;
28 vmci_event_cb callback;
29 void *callback_data;
30 vmci_list_item(vmci_subscription) subscriber_list_item;
31 };
32
33 static struct vmci_subscription *vmci_event_find(vmci_id sub_id);
34 static int vmci_event_deliver(struct vmci_event_msg *event_msg);
35 static int vmci_event_register_subscription(struct vmci_subscription *sub,
36 vmci_event_type event, uint32_t flags,
37 vmci_event_cb callback, void *callback_data);
38 static struct vmci_subscription *vmci_event_unregister_subscription(
39 vmci_id sub_id);
40
41 static vmci_list(vmci_subscription) subscriber_array[VMCI_EVENT_MAX];
42 static vmci_lock subscriber_lock;
43
44 struct vmci_delayed_event_info {
45 struct vmci_subscription *sub;
46 uint8_t event_payload[sizeof(struct vmci_event_data_max)];
47 };
48
49 struct vmci_event_ref {
50 struct vmci_subscription *sub;
51 vmci_list_item(vmci_event_ref) list_item;
52 };
53
54 /*
55 *------------------------------------------------------------------------------
56 *
57 * vmci_event_init --
58 *
59 * General init code.
60 *
61 * Results:
62 * VMCI_SUCCESS on success, appropriate error code otherwise.
63 *
64 * Side effects:
65 * None.
66 *
67 *------------------------------------------------------------------------------
68 */
69
70 int
71 vmci_event_init(void)
72 {
73 int i;
74
75 for (i = 0; i < VMCI_EVENT_MAX; i++)
76 vmci_list_init(&subscriber_array[i]);
77
78 return (vmci_init_lock(&subscriber_lock, "VMCI Event subscriber lock"));
79 }
80
81 /*
82 *------------------------------------------------------------------------------
83 *
84 * vmci_event_exit --
85 *
86 * General exit code.
87 *
88 * Results:
89 * None.
90 *
91 * Side effects:
92 * None.
93 *
94 *------------------------------------------------------------------------------
95 */
96
97 void
98 vmci_event_exit(void)
99 {
100 struct vmci_subscription *iter, *iter_2;
101 vmci_event_type e;
102
103 /* We free all memory at exit. */
104 for (e = 0; e < VMCI_EVENT_MAX; e++) {
105 vmci_list_scan_safe(iter, &subscriber_array[e],
106 subscriber_list_item, iter_2) {
107 /*
108 * We should never get here because all events should
109 * have been unregistered before we try to unload the
110 * driver module. Also, delayed callbacks could still
111 * be firing so this cleanup would not be safe. Still
112 * it is better to free the memory than not ... so we
113 * leave this code in just in case....
114 */
115 ASSERT(false);
116
117 vmci_free_kernel_mem(iter, sizeof(*iter));
118 }
119 }
120 vmci_cleanup_lock(&subscriber_lock);
121 }
122
123 /*
124 *------------------------------------------------------------------------------
125 *
126 * vmci_event_sync --
127 *
128 * Use this as a synchronization point when setting globals, for example,
129 * during device shutdown.
130 *
131 * Results:
132 * true.
133 *
134 * Side effects:
135 * None.
136 *
137 *------------------------------------------------------------------------------
138 */
139
140 void
141 vmci_event_sync(void)
142 {
143
144 vmci_grab_lock_bh(&subscriber_lock);
145 vmci_release_lock_bh(&subscriber_lock);
146 }
147
148 /*
149 *------------------------------------------------------------------------------
150 *
151 * vmci_event_check_host_capabilities --
152 *
153 * Verify that the host supports the hypercalls we need. If it does not,
154 * try to find fallback hypercalls and use those instead.
155 *
156 * Results:
157 * true if required hypercalls (or fallback hypercalls) are
158 * supported by the host, false otherwise.
159 *
160 * Side effects:
161 * None.
162 *
163 *------------------------------------------------------------------------------
164 */
165
166 bool
167 vmci_event_check_host_capabilities(void)
168 {
169
170 /* vmci_event does not require any hypercalls. */
171 return (true);
172 }
173
174 /*
175 *------------------------------------------------------------------------------
176 *
177 * vmci_event_get --
178 *
179 * Gets a reference to the given struct vmci_subscription.
180 *
181 * Results:
182 * None.
183 *
184 * Side effects:
185 * None.
186 *
187 *------------------------------------------------------------------------------
188 */
189
190 static void
191 vmci_event_get(struct vmci_subscription *entry)
192 {
193
194 ASSERT(entry);
195
196 entry->ref_count++;
197 }
198
199 /*
200 *------------------------------------------------------------------------------
201 *
202 * vmci_event_release --
203 *
204 * Releases the given struct vmci_subscription.
205 *
206 * Results:
207 * None.
208 *
209 * Side effects:
210 * Fires the destroy event if the reference count has gone to zero.
211 *
212 *------------------------------------------------------------------------------
213 */
214
215 static void
216 vmci_event_release(struct vmci_subscription *entry)
217 {
218
219 ASSERT(entry);
220 ASSERT(entry->ref_count > 0);
221
222 entry->ref_count--;
223 if (entry->ref_count == 0)
224 vmci_signal_event(&entry->destroy_event);
225 }
226
227 /*
228 *------------------------------------------------------------------------------
229 *
230 * event_release_cb --
231 *
232 * Callback to release the event entry reference. It is called by the
233 * vmci_wait_on_event function before it blocks.
234 *
235 * Result:
236 * None.
237 *
238 * Side effects:
239 * None.
240 *
241 *------------------------------------------------------------------------------
242 */
243
244 static int
245 event_release_cb(void *client_data)
246 {
247 struct vmci_subscription *sub = (struct vmci_subscription *)client_data;
248
249 ASSERT(sub);
250
251 vmci_grab_lock_bh(&subscriber_lock);
252 vmci_event_release(sub);
253 vmci_release_lock_bh(&subscriber_lock);
254
255 return (0);
256 }
257
258 /*
259 *------------------------------------------------------------------------------
260 *
261 * vmci_event_find --
262 *
263 * Find entry. Assumes lock is held.
264 *
265 * Results:
266 * Entry if found, NULL if not.
267 *
268 * Side effects:
269 * Increments the struct vmci_subscription refcount if an entry is found.
270 *
271 *------------------------------------------------------------------------------
272 */
273
274 static struct vmci_subscription *
275 vmci_event_find(vmci_id sub_id)
276 {
277 struct vmci_subscription *iter;
278 vmci_event_type e;
279
280 for (e = 0; e < VMCI_EVENT_MAX; e++) {
281 vmci_list_scan(iter, &subscriber_array[e],
282 subscriber_list_item) {
283 if (iter->id == sub_id) {
284 vmci_event_get(iter);
285 return (iter);
286 }
287 }
288 }
289 return (NULL);
290 }
291
292 /*
293 *------------------------------------------------------------------------------
294 *
295 * vmci_event_delayed_dispatch_cb --
296 *
297 * Calls the specified callback in a delayed context.
298 *
299 * Results:
300 * None.
301 *
302 * Side effects:
303 * None.
304 *
305 *------------------------------------------------------------------------------
306 */
307
308 static void
309 vmci_event_delayed_dispatch_cb(void *data)
310 {
311 struct vmci_delayed_event_info *event_info;
312 struct vmci_subscription *sub;
313 struct vmci_event_data *ed;
314
315 event_info = (struct vmci_delayed_event_info *)data;
316
317 ASSERT(event_info);
318 ASSERT(event_info->sub);
319
320 sub = event_info->sub;
321 ed = (struct vmci_event_data *)event_info->event_payload;
322
323 sub->callback(sub->id, ed, sub->callback_data);
324
325 vmci_grab_lock_bh(&subscriber_lock);
326 vmci_event_release(sub);
327 vmci_release_lock_bh(&subscriber_lock);
328
329 vmci_free_kernel_mem(event_info, sizeof(*event_info));
330 }
331
332 /*
333 *------------------------------------------------------------------------------
334 *
335 * vmci_event_deliver --
336 *
337 * Actually delivers the events to the subscribers.
338 *
339 * Results:
340 * None.
341 *
342 * Side effects:
343 * The callback function for each subscriber is invoked.
344 *
345 *------------------------------------------------------------------------------
346 */
347
348 static int
349 vmci_event_deliver(struct vmci_event_msg *event_msg)
350 {
351 struct vmci_subscription *iter;
352 int err = VMCI_SUCCESS;
353
354 vmci_list(vmci_event_ref) no_delay_list;
355 vmci_list_init(&no_delay_list);
356
357 ASSERT(event_msg);
358
359 vmci_grab_lock_bh(&subscriber_lock);
360 vmci_list_scan(iter, &subscriber_array[event_msg->event_data.event],
361 subscriber_list_item) {
362 if (iter->run_delayed) {
363 struct vmci_delayed_event_info *event_info;
364 if ((event_info =
365 vmci_alloc_kernel_mem(sizeof(*event_info),
366 VMCI_MEMORY_ATOMIC)) == NULL) {
367 err = VMCI_ERROR_NO_MEM;
368 goto out;
369 }
370
371 vmci_event_get(iter);
372
373 memset(event_info, 0, sizeof(*event_info));
374 memcpy(event_info->event_payload,
375 VMCI_DG_PAYLOAD(event_msg),
376 (size_t)event_msg->hdr.payload_size);
377 event_info->sub = iter;
378 err =
379 vmci_schedule_delayed_work(
380 vmci_event_delayed_dispatch_cb, event_info);
381 if (err != VMCI_SUCCESS) {
382 vmci_event_release(iter);
383 vmci_free_kernel_mem(
384 event_info, sizeof(*event_info));
385 goto out;
386 }
387
388 } else {
389 struct vmci_event_ref *event_ref;
390
391 /*
392 * We construct a local list of subscribers and release
393 * subscriber_lock before invoking the callbacks. This
394 * is similar to delayed callbacks, but callbacks are
395 * invoked right away here.
396 */
397 if ((event_ref = vmci_alloc_kernel_mem(
398 sizeof(*event_ref), VMCI_MEMORY_ATOMIC)) == NULL) {
399 err = VMCI_ERROR_NO_MEM;
400 goto out;
401 }
402
403 vmci_event_get(iter);
404 event_ref->sub = iter;
405 vmci_list_insert(&no_delay_list, event_ref, list_item);
406 }
407 }
408
409 out:
410 vmci_release_lock_bh(&subscriber_lock);
411
412 if (!vmci_list_empty(&no_delay_list)) {
413 struct vmci_event_data *ed;
414 struct vmci_event_ref *iter;
415 struct vmci_event_ref *iter_2;
416
417 vmci_list_scan_safe(iter, &no_delay_list, list_item, iter_2) {
418 struct vmci_subscription *cur;
419 uint8_t event_payload[sizeof(
420 struct vmci_event_data_max)];
421
422 cur = iter->sub;
423
424 /*
425 * We set event data before each callback to ensure
426 * isolation.
427 */
428 memset(event_payload, 0, sizeof(event_payload));
429 memcpy(event_payload, VMCI_DG_PAYLOAD(event_msg),
430 (size_t)event_msg->hdr.payload_size);
431 ed = (struct vmci_event_data *)event_payload;
432 cur->callback(cur->id, ed, cur->callback_data);
433
434 vmci_grab_lock_bh(&subscriber_lock);
435 vmci_event_release(cur);
436 vmci_release_lock_bh(&subscriber_lock);
437 vmci_free_kernel_mem(iter, sizeof(*iter));
438 }
439 }
440
441 return (err);
442 }
443
444 /*
445 *------------------------------------------------------------------------------
446 *
447 * vmci_event_dispatch --
448 *
449 * Dispatcher for the VMCI_EVENT_RECEIVE datagrams. Calls all
450 * subscribers for given event.
451 *
452 * Results:
453 * VMCI_SUCCESS on success, error code otherwise.
454 *
455 * Side effects:
456 * None.
457 *
458 *------------------------------------------------------------------------------
459 */
460
461 int
462 vmci_event_dispatch(struct vmci_datagram *msg)
463 {
464 struct vmci_event_msg *event_msg = (struct vmci_event_msg *)msg;
465
466 ASSERT(msg &&
467 msg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
468 msg->dst.resource == VMCI_EVENT_HANDLER);
469
470 if (msg->payload_size < sizeof(vmci_event_type) ||
471 msg->payload_size > sizeof(struct vmci_event_data_max))
472 return (VMCI_ERROR_INVALID_ARGS);
473
474 if (!VMCI_EVENT_VALID(event_msg->event_data.event))
475 return (VMCI_ERROR_EVENT_UNKNOWN);
476
477 vmci_event_deliver(event_msg);
478
479 return (VMCI_SUCCESS);
480 }
481
482 /*
483 *------------------------------------------------------------------------------
484 *
485 * vmci_event_register_subscription --
486 *
487 * Initialize and add subscription to subscriber list.
488 *
489 * Results:
490 * VMCI_SUCCESS on success, error code otherwise.
491 *
492 * Side effects:
493 * None.
494 *
495 *------------------------------------------------------------------------------
496 */
497
498 static int
499 vmci_event_register_subscription(struct vmci_subscription *sub,
500 vmci_event_type event, uint32_t flags, vmci_event_cb callback,
501 void *callback_data)
502 {
503 #define VMCI_EVENT_MAX_ATTEMPTS 10
504 static vmci_id subscription_id = 0;
505 int result;
506 uint32_t attempts = 0;
507 bool success;
508
509 ASSERT(sub);
510
511 if (!VMCI_EVENT_VALID(event) || callback == NULL) {
512 VMCI_LOG_DEBUG(LGPFX"Failed to subscribe to event"
513 " (type=%d) (callback=%p) (data=%p).\n",
514 event, callback, callback_data);
515 return (VMCI_ERROR_INVALID_ARGS);
516 }
517
518 if (!vmci_can_schedule_delayed_work()) {
519 /*
520 * If the platform doesn't support delayed work callbacks then
521 * don't allow registration for them.
522 */
523 if (flags & VMCI_FLAG_EVENT_DELAYED_CB)
524 return (VMCI_ERROR_INVALID_ARGS);
525 sub->run_delayed = false;
526 } else {
527 /*
528 * The platform supports delayed work callbacks. Honor the
529 * requested flags
530 */
531 sub->run_delayed = (flags & VMCI_FLAG_EVENT_DELAYED_CB) ?
532 true : false;
533 }
534
535 sub->ref_count = 1;
536 sub->event = event;
537 sub->callback = callback;
538 sub->callback_data = callback_data;
539
540 vmci_grab_lock_bh(&subscriber_lock);
541
542 for (success = false, attempts = 0;
543 success == false && attempts < VMCI_EVENT_MAX_ATTEMPTS;
544 attempts++) {
545 struct vmci_subscription *existing_sub = NULL;
546
547 /*
548 * We try to get an id a couple of time before claiming we are
549 * out of resources.
550 */
551 sub->id = ++subscription_id;
552
553 /* Test for duplicate id. */
554 existing_sub = vmci_event_find(sub->id);
555 if (existing_sub == NULL) {
556 /* We succeeded if we didn't find a duplicate. */
557 success = true;
558 } else
559 vmci_event_release(existing_sub);
560 }
561
562 if (success) {
563 vmci_create_event(&sub->destroy_event);
564 vmci_list_insert(&subscriber_array[event], sub,
565 subscriber_list_item);
566 result = VMCI_SUCCESS;
567 } else
568 result = VMCI_ERROR_NO_RESOURCES;
569
570 vmci_release_lock_bh(&subscriber_lock);
571 return (result);
572 #undef VMCI_EVENT_MAX_ATTEMPTS
573 }
574
575 /*
576 *------------------------------------------------------------------------------
577 *
578 * vmci_event_unregister_subscription --
579 *
580 * Remove subscription from subscriber list.
581 *
582 * Results:
583 * struct vmci_subscription when found, NULL otherwise.
584 *
585 * Side effects:
586 * None.
587 *
588 *------------------------------------------------------------------------------
589 */
590
591 static struct vmci_subscription *
592 vmci_event_unregister_subscription(vmci_id sub_id)
593 {
594 struct vmci_subscription *s;
595
596 if (!vmci_initialized_lock(&subscriber_lock))
597 return NULL;
598
599 vmci_grab_lock_bh(&subscriber_lock);
600 s = vmci_event_find(sub_id);
601 if (s != NULL) {
602 vmci_event_release(s);
603 vmci_list_remove(s, subscriber_list_item);
604 }
605 vmci_release_lock_bh(&subscriber_lock);
606
607 if (s != NULL) {
608 vmci_wait_on_event(&s->destroy_event, event_release_cb, s);
609 vmci_destroy_event(&s->destroy_event);
610 }
611
612 return (s);
613 }
614
615 /*
616 *------------------------------------------------------------------------------
617 *
618 * vmci_event_subscribe --
619 *
620 * Subscribe to given event. The callback specified can be fired in
621 * different contexts depending on what flag is specified while registering.
622 * If flags contains VMCI_FLAG_EVENT_NONE then the callback is fired with
623 * the subscriber lock held (and BH context on the guest). If flags contain
624 * VMCI_FLAG_EVENT_DELAYED_CB then the callback is fired with no locks held
625 * in thread context. This is useful because other vmci_event functions can
626 * be called, but it also increases the chances that an event will be
627 * dropped.
628 *
629 * Results:
630 * VMCI_SUCCESS on success, error code otherwise.
631 *
632 * Side effects:
633 * None.
634 *
635 *------------------------------------------------------------------------------
636 */
637
638 int
639 vmci_event_subscribe(vmci_event_type event, vmci_event_cb callback,
640 void *callback_data, vmci_id *subscription_id)
641 {
642 int retval;
643 uint32_t flags = VMCI_FLAG_EVENT_NONE;
644 struct vmci_subscription *s = NULL;
645
646 if (subscription_id == NULL) {
647 VMCI_LOG_DEBUG(LGPFX"Invalid subscription (NULL).\n");
648 return (VMCI_ERROR_INVALID_ARGS);
649 }
650
651 s = vmci_alloc_kernel_mem(sizeof(*s), VMCI_MEMORY_NORMAL);
652 if (s == NULL)
653 return (VMCI_ERROR_NO_MEM);
654
655 retval = vmci_event_register_subscription(s, event, flags,
656 callback, callback_data);
657 if (retval < VMCI_SUCCESS) {
658 vmci_free_kernel_mem(s, sizeof(*s));
659 return (retval);
660 }
661
662 *subscription_id = s->id;
663 return (retval);
664 }
665
666 /*
667 *------------------------------------------------------------------------------
668 *
669 * vmci_event_unsubscribe --
670 *
671 * Unsubscribe to given event. Removes it from list and frees it.
672 * Will return callback_data if requested by caller.
673 *
674 * Results:
675 * VMCI_SUCCESS on success, error code otherwise.
676 *
677 * Side effects:
678 * None.
679 *
680 *------------------------------------------------------------------------------
681 */
682
683 int
684 vmci_event_unsubscribe(vmci_id sub_id)
685 {
686 struct vmci_subscription *s;
687
688 /*
689 * Return subscription. At this point we know noone else is accessing
690 * the subscription so we can free it.
691 */
692 s = vmci_event_unregister_subscription(sub_id);
693 if (s == NULL)
694 return (VMCI_ERROR_NOT_FOUND);
695 vmci_free_kernel_mem(s, sizeof(*s));
696
697 return (VMCI_SUCCESS);
698 }
Cache object: d8576a37cafa414e79a6ba69cd8a6452
|