]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vmware/vmci/vmci_event.c
contrib/bc: merge version 5.1.0 from vendor branch
[FreeBSD/FreeBSD.git] / sys / dev / vmware / vmci / vmci_event.c
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         vmci_grab_lock_bh(&subscriber_lock);
597         s = vmci_event_find(sub_id);
598         if (s != NULL) {
599                 vmci_event_release(s);
600                 vmci_list_remove(s, subscriber_list_item);
601         }
602         vmci_release_lock_bh(&subscriber_lock);
603
604         if (s != NULL) {
605                 vmci_wait_on_event(&s->destroy_event, event_release_cb, s);
606                 vmci_destroy_event(&s->destroy_event);
607         }
608
609         return (s);
610 }
611
612 /*
613  *------------------------------------------------------------------------------
614  *
615  * vmci_event_subscribe --
616  *
617  *     Subscribe to given event. The callback specified can be fired in
618  *     different contexts depending on what flag is specified while registering.
619  *     If flags contains VMCI_FLAG_EVENT_NONE then the callback is fired with
620  *     the subscriber lock held (and BH context on the guest). If flags contain
621  *     VMCI_FLAG_EVENT_DELAYED_CB then the callback is fired with no locks held
622  *     in thread context. This is useful because other vmci_event functions can
623  *     be called, but it also increases the chances that an event will be
624  *     dropped.
625  *
626  * Results:
627  *     VMCI_SUCCESS on success, error code otherwise.
628  *
629  * Side effects:
630  *     None.
631  *
632  *------------------------------------------------------------------------------
633  */
634
635 int
636 vmci_event_subscribe(vmci_event_type event, vmci_event_cb callback,
637     void *callback_data, vmci_id *subscription_id)
638 {
639         int retval;
640         uint32_t flags = VMCI_FLAG_EVENT_NONE;
641         struct vmci_subscription *s = NULL;
642
643         if (subscription_id == NULL) {
644                 VMCI_LOG_DEBUG(LGPFX"Invalid subscription (NULL).\n");
645                 return (VMCI_ERROR_INVALID_ARGS);
646         }
647
648         s = vmci_alloc_kernel_mem(sizeof(*s), VMCI_MEMORY_NORMAL);
649         if (s == NULL)
650                 return (VMCI_ERROR_NO_MEM);
651
652         retval = vmci_event_register_subscription(s, event, flags,
653             callback, callback_data);
654         if (retval < VMCI_SUCCESS) {
655                 vmci_free_kernel_mem(s, sizeof(*s));
656                 return (retval);
657         }
658
659         *subscription_id = s->id;
660         return (retval);
661 }
662
663 /*
664  *------------------------------------------------------------------------------
665  *
666  * vmci_event_unsubscribe --
667  *
668  *     Unsubscribe to given event. Removes it from list and frees it.
669  *     Will return callback_data if requested by caller.
670  *
671  * Results:
672  *     VMCI_SUCCESS on success, error code otherwise.
673  *
674  * Side effects:
675  *     None.
676  *
677  *------------------------------------------------------------------------------
678  */
679
680 int
681 vmci_event_unsubscribe(vmci_id sub_id)
682 {
683         struct vmci_subscription *s;
684
685         /*
686          * Return subscription. At this point we know noone else is accessing
687          * the subscription so we can free it.
688          */
689         s = vmci_event_unregister_subscription(sub_id);
690         if (s == NULL)
691                 return (VMCI_ERROR_NOT_FOUND);
692         vmci_free_kernel_mem(s, sizeof(*s));
693
694         return (VMCI_SUCCESS);
695 }