From: Iouri Tarassov <[email protected]>

Implement ioctls to submit operations with compute device
sync objects:
  - the LX_DXSIGNALSYNCHRONIZATIONOBJECT ioctl.
    The ioctl is used to submit a signal to a sync object.
  - the LX_DXWAITFORSYNCHRONIZATIONOBJECT ioctl.
    The ioctl is used to submit a wait for a sync object
  - the LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU ioctl
    The ioctl is used to signal to a monitored fence sync object
    from a CPU thread.
  - the LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU ioctl.
    The ioctl is used to submit a signal to a monitored fence
    sync object..
  - the LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2 ioctl.
    The ioctl is used to submit a signal to a monitored fence
    sync object.
  - the LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU ioctl.
    The ioctl is used to submit a wait for a monitored fence
    sync object.

Compute device synchronization objects are used to synchronize
execution of DMA buffers between different execution contexts.
Operations with sync objects include "signal" and "wait". A wait
for a sync object is satisfied when the sync object is signaled.

A signal operation could be submitted to a compute device context or
the sync object could be signaled by a CPU thread.

To improve performance, submitting operations to the host is done
asynchronously when the host supports it.

Signed-off-by: Iouri Tarassov <[email protected]>
[kms: forward port to 6.6 from 6.1. No code changes made.]
Signed-off-by: Kelsey Steele <[email protected]>
---
 drivers/hv/dxgkrnl/dxgadapter.c |  38 +-
 drivers/hv/dxgkrnl/dxgkrnl.h    |  62 +++
 drivers/hv/dxgkrnl/dxgmodule.c  | 102 ++++-
 drivers/hv/dxgkrnl/dxgvmbus.c   | 219 +++++++++-
 drivers/hv/dxgkrnl/dxgvmbus.h   |  48 +++
 drivers/hv/dxgkrnl/ioctl.c      | 702 +++++++++++++++++++++++++++++++-
 drivers/hv/dxgkrnl/misc.h       |   2 +
 include/uapi/misc/d3dkmthk.h    | 159 ++++++++
 8 files changed, 1311 insertions(+), 21 deletions(-)

diff --git a/drivers/hv/dxgkrnl/dxgadapter.c b/drivers/hv/dxgkrnl/dxgadapter.c
index d2f2b96527e6..04d827a15c54 100644
--- a/drivers/hv/dxgkrnl/dxgadapter.c
+++ b/drivers/hv/dxgkrnl/dxgadapter.c
@@ -249,7 +249,7 @@ void dxgdevice_stop(struct dxgdevice *device)
        struct dxgallocation *alloc;
        struct dxgsyncobject *syncobj;
 
-       DXG_TRACE("Destroying device: %p", device);
+       DXG_TRACE("Stopping device: %p", device);
        dxgdevice_acquire_alloc_list_lock(device);
        list_for_each_entry(alloc, &device->alloc_list_head, alloc_list_entry) {
                dxgallocation_stop(alloc);
@@ -743,15 +743,13 @@ void dxgallocation_destroy(struct dxgallocation *alloc)
        }
 #ifdef _MAIN_KERNEL_
        if (alloc->gpadl.gpadl_handle) {
-               DXG_TRACE("Teardown gpadl %d",
-                       alloc->gpadl.gpadl_handle);
+               DXG_TRACE("Teardown gpadl %d", alloc->gpadl.gpadl_handle);
                vmbus_teardown_gpadl(dxgglobal_get_vmbus(), &alloc->gpadl);
                alloc->gpadl.gpadl_handle = 0;
        }
 else
        if (alloc->gpadl) {
-               DXG_TRACE("Teardown gpadl %d",
-                       alloc->gpadl);
+               DXG_TRACE("Teardown gpadl %d", alloc->gpadl);
                vmbus_teardown_gpadl(dxgglobal_get_vmbus(), alloc->gpadl);
                alloc->gpadl = 0;
        }
@@ -901,6 +899,13 @@ struct dxgsyncobject *dxgsyncobject_create(struct 
dxgprocess *process,
        case _D3DDDI_PERIODIC_MONITORED_FENCE:
                syncobj->monitored_fence = 1;
                break;
+       case _D3DDDI_CPU_NOTIFICATION:
+               syncobj->cpu_event = 1;
+               syncobj->host_event = kzalloc(sizeof(*syncobj->host_event),
+                                       GFP_KERNEL);
+               if (syncobj->host_event == NULL)
+                       goto cleanup;
+               break;
        default:
                break;
        }
@@ -928,6 +933,8 @@ struct dxgsyncobject *dxgsyncobject_create(struct 
dxgprocess *process,
        DXG_TRACE("Syncobj created: %p", syncobj);
        return syncobj;
 cleanup:
+       if (syncobj->host_event)
+               kfree(syncobj->host_event);
        if (syncobj)
                kfree(syncobj);
        return NULL;
@@ -937,6 +944,7 @@ void dxgsyncobject_destroy(struct dxgprocess *process,
                           struct dxgsyncobject *syncobj)
 {
        int destroyed;
+       struct dxghosteventcpu *host_event;
 
        DXG_TRACE("Destroying syncobj: %p", syncobj);
 
@@ -955,6 +963,16 @@ void dxgsyncobject_destroy(struct dxgprocess *process,
                }
                hmgrtable_unlock(&process->handle_table, DXGLOCK_EXCL);
 
+               if (syncobj->cpu_event) {
+                       host_event = syncobj->host_event;
+                       if (host_event->cpu_event) {
+                               eventfd_ctx_put(host_event->cpu_event);
+                               if (host_event->hdr.event_id)
+                                       dxgglobal_remove_host_event(
+                                               &host_event->hdr);
+                               host_event->cpu_event = NULL;
+                       }
+               }
                if (syncobj->monitored_fence)
                        dxgdevice_remove_syncobj(syncobj);
                else
@@ -971,16 +989,14 @@ void dxgsyncobject_destroy(struct dxgprocess *process,
 void dxgsyncobject_stop(struct dxgsyncobject *syncobj)
 {
        int stopped = test_and_set_bit(1, &syncobj->flags);
+       int ret;
 
        if (!stopped) {
                DXG_TRACE("Stopping syncobj");
                if (syncobj->monitored_fence) {
                        if (syncobj->mapped_address) {
-                               int ret =
-                                   dxg_unmap_iospace(syncobj->mapped_address,
-                                                     PAGE_SIZE);
-
-                               (void)ret;
+                               ret = dxg_unmap_iospace(syncobj->mapped_address,
+                                                       PAGE_SIZE);
                                DXG_TRACE("unmap fence %d %p",
                                        ret, syncobj->mapped_address);
                                syncobj->mapped_address = NULL;
@@ -994,5 +1010,7 @@ void dxgsyncobject_release(struct kref *refcount)
        struct dxgsyncobject *syncobj;
 
        syncobj = container_of(refcount, struct dxgsyncobject, syncobj_kref);
+       if (syncobj->host_event)
+               kfree(syncobj->host_event);
        kfree(syncobj);
 }
diff --git a/drivers/hv/dxgkrnl/dxgkrnl.h b/drivers/hv/dxgkrnl/dxgkrnl.h
index 1b9410c9152b..8431523f42de 100644
--- a/drivers/hv/dxgkrnl/dxgkrnl.h
+++ b/drivers/hv/dxgkrnl/dxgkrnl.h
@@ -101,6 +101,29 @@ int dxgvmbuschannel_init(struct dxgvmbuschannel *ch, 
struct hv_device *hdev);
 void dxgvmbuschannel_destroy(struct dxgvmbuschannel *ch);
 void dxgvmbuschannel_receive(void *ctx);
 
+/*
+ * The structure describes an event, which will be signaled by
+ * a message from host.
+ */
+enum dxghosteventtype {
+       dxghostevent_cpu_event = 1,
+};
+
+struct dxghostevent {
+       struct list_head        host_event_list_entry;
+       u64                     event_id;
+       enum dxghosteventtype   event_type;
+};
+
+struct dxghosteventcpu {
+       struct dxghostevent     hdr;
+       struct dxgprocess       *process;
+       struct eventfd_ctx      *cpu_event;
+       struct completion       *completion_event;
+       bool                    destroy_after_signal;
+       bool                    remove_from_list;
+};
+
 /*
  * This is GPU synchronization object, which is used to synchronize execution
  * between GPU contextx/hardware queues or for tracking GPU execution progress.
@@ -130,6 +153,8 @@ struct dxgsyncobject {
         */
        struct dxgdevice                *device;
        struct dxgprocess               *process;
+       /* Used by D3DDDI_CPU_NOTIFICATION objects */
+       struct dxghosteventcpu          *host_event;
        /* CPU virtual address of the fence value for "device" syncobjects */
        void                            *mapped_address;
        /* Handle in the process handle table */
@@ -144,6 +169,7 @@ struct dxgsyncobject {
                        u32             stopped:1;
                        /* device syncobject */
                        u32             monitored_fence:1;
+                       u32             cpu_event:1;
                        u32             shared:1;
                        u32             reserved:27;
                };
@@ -206,6 +232,11 @@ struct dxgglobal {
        /* protects the dxgprocess_adapter lists */
        struct mutex            process_adapter_mutex;
 
+       /*  list of events, waiting to be signaled by the host */
+       struct list_head        host_event_list_head;
+       spinlock_t              host_event_list_mutex;
+       atomic64_t              host_event_id;
+
        bool                    global_channel_initialized;
        bool                    async_msg_enabled;
        bool                    misc_registered;
@@ -228,6 +259,11 @@ struct vmbus_channel *dxgglobal_get_vmbus(void);
 struct dxgvmbuschannel *dxgglobal_get_dxgvmbuschannel(void);
 void dxgglobal_acquire_process_adapter_lock(void);
 void dxgglobal_release_process_adapter_lock(void);
+void dxgglobal_add_host_event(struct dxghostevent *hostevent);
+void dxgglobal_remove_host_event(struct dxghostevent *hostevent);
+u64 dxgglobal_new_host_event_id(void);
+void dxgglobal_signal_host_event(u64 event_id);
+struct dxghostevent *dxgglobal_get_host_event(u64 event_id);
 int dxgglobal_acquire_channel_lock(void);
 void dxgglobal_release_channel_lock(void);
 
@@ -594,6 +630,31 @@ int dxgvmb_send_create_sync_object(struct dxgprocess *pr,
                                   *args, struct dxgsyncobject *so);
 int dxgvmb_send_destroy_sync_object(struct dxgprocess *pr,
                                    struct d3dkmthandle h);
+int dxgvmb_send_signal_sync_object(struct dxgprocess *process,
+                                  struct dxgadapter *adapter,
+                                  struct d3dddicb_signalflags flags,
+                                  u64 legacy_fence_value,
+                                  struct d3dkmthandle context,
+                                  u32 object_count,
+                                  struct d3dkmthandle *object,
+                                  u32 context_count,
+                                  struct d3dkmthandle *contexts,
+                                  u32 fence_count, u64 *fences,
+                                  struct eventfd_ctx *cpu_event,
+                                  struct d3dkmthandle device);
+int dxgvmb_send_wait_sync_object_gpu(struct dxgprocess *process,
+                                    struct dxgadapter *adapter,
+                                    struct d3dkmthandle context,
+                                    u32 object_count,
+                                    struct d3dkmthandle *objects,
+                                    u64 *fences,
+                                    bool legacy_fence);
+int dxgvmb_send_wait_sync_object_cpu(struct dxgprocess *process,
+                                    struct dxgadapter *adapter,
+                                    struct
+                                    d3dkmt_waitforsynchronizationobjectfromcpu
+                                    *args,
+                                    u64 cpu_event);
 int dxgvmb_send_query_adapter_info(struct dxgprocess *process,
                                   struct dxgadapter *adapter,
                                   struct d3dkmt_queryadapterinfo *args);
@@ -609,6 +670,7 @@ int dxgvmb_send_async_msg(struct dxgvmbuschannel *channel,
                          void *command,
                          u32 cmd_size);
 
+void signal_host_cpu_event(struct dxghostevent *eventhdr);
 int ntstatus2int(struct ntstatus status);
 
 #ifdef DEBUG
diff --git a/drivers/hv/dxgkrnl/dxgmodule.c b/drivers/hv/dxgkrnl/dxgmodule.c
index 9bc8931c5043..5a5ca8791d27 100644
--- a/drivers/hv/dxgkrnl/dxgmodule.c
+++ b/drivers/hv/dxgkrnl/dxgmodule.c
@@ -123,6 +123,102 @@ static struct dxgadapter *find_adapter(struct winluid 
*luid)
        return adapter;
 }
 
+void dxgglobal_add_host_event(struct dxghostevent *event)
+{
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       spin_lock_irq(&dxgglobal->host_event_list_mutex);
+       list_add_tail(&event->host_event_list_entry,
+                     &dxgglobal->host_event_list_head);
+       spin_unlock_irq(&dxgglobal->host_event_list_mutex);
+}
+
+void dxgglobal_remove_host_event(struct dxghostevent *event)
+{
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       spin_lock_irq(&dxgglobal->host_event_list_mutex);
+       if (event->host_event_list_entry.next != NULL) {
+               list_del(&event->host_event_list_entry);
+               event->host_event_list_entry.next = NULL;
+       }
+       spin_unlock_irq(&dxgglobal->host_event_list_mutex);
+}
+
+void signal_host_cpu_event(struct dxghostevent *eventhdr)
+{
+       struct  dxghosteventcpu *event = (struct  dxghosteventcpu *)eventhdr;
+
+       if (event->remove_from_list ||
+               event->destroy_after_signal) {
+               list_del(&eventhdr->host_event_list_entry);
+               eventhdr->host_event_list_entry.next = NULL;
+       }
+       if (event->cpu_event) {
+               DXG_TRACE("signal cpu event");
+               eventfd_signal(event->cpu_event, 1);
+               if (event->destroy_after_signal)
+                       eventfd_ctx_put(event->cpu_event);
+       } else {
+               DXG_TRACE("signal completion");
+               complete(event->completion_event);
+       }
+       if (event->destroy_after_signal) {
+               DXG_TRACE("destroying event %p", event);
+               kfree(event);
+       }
+}
+
+void dxgglobal_signal_host_event(u64 event_id)
+{
+       struct dxghostevent *event;
+       unsigned long flags;
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       DXG_TRACE("Signaling host event %lld", event_id);
+
+       spin_lock_irqsave(&dxgglobal->host_event_list_mutex, flags);
+       list_for_each_entry(event, &dxgglobal->host_event_list_head,
+                           host_event_list_entry) {
+               if (event->event_id == event_id) {
+                       DXG_TRACE("found event to signal");
+                       if (event->event_type == dxghostevent_cpu_event)
+                               signal_host_cpu_event(event);
+                       else
+                               DXG_ERR("Unknown host event type");
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&dxgglobal->host_event_list_mutex, flags);
+}
+
+struct dxghostevent *dxgglobal_get_host_event(u64 event_id)
+{
+       struct dxghostevent *entry;
+       struct dxghostevent *event = NULL;
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       spin_lock_irq(&dxgglobal->host_event_list_mutex);
+       list_for_each_entry(entry, &dxgglobal->host_event_list_head,
+                           host_event_list_entry) {
+               if (entry->event_id == event_id) {
+                       list_del(&entry->host_event_list_entry);
+                       entry->host_event_list_entry.next = NULL;
+                       event = entry;
+                       break;
+               }
+       }
+       spin_unlock_irq(&dxgglobal->host_event_list_mutex);
+       return event;
+}
+
+u64 dxgglobal_new_host_event_id(void)
+{
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       return atomic64_inc_return(&dxgglobal->host_event_id);
+}
+
 void dxgglobal_acquire_process_adapter_lock(void)
 {
        struct dxgglobal *dxgglobal = dxggbl();
@@ -720,12 +816,16 @@ static struct dxgglobal *dxgglobal_create(void)
        INIT_LIST_HEAD(&dxgglobal->vgpu_ch_list_head);
        INIT_LIST_HEAD(&dxgglobal->adapter_list_head);
        init_rwsem(&dxgglobal->adapter_list_lock);
-
        init_rwsem(&dxgglobal->channel_lock);
 
+       INIT_LIST_HEAD(&dxgglobal->host_event_list_head);
+       spin_lock_init(&dxgglobal->host_event_list_mutex);
+       atomic64_set(&dxgglobal->host_event_id, 1);
+
 #ifdef DEBUG
        dxgk_validate_ioctls();
 #endif
+
        return dxgglobal;
 }
 
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.c b/drivers/hv/dxgkrnl/dxgvmbus.c
index d323afc85249..6b2dea24a509 100644
--- a/drivers/hv/dxgkrnl/dxgvmbus.c
+++ b/drivers/hv/dxgkrnl/dxgvmbus.c
@@ -281,6 +281,22 @@ static void command_vm_to_host_init1(struct 
dxgkvmb_command_vm_to_host *command,
        command->channel_type = DXGKVMB_VM_TO_HOST;
 }
 
+static void signal_guest_event(struct dxgkvmb_command_host_to_vm *packet,
+                              u32 packet_length)
+{
+       struct dxgkvmb_command_signalguestevent *command = (void *)packet;
+
+       if (packet_length < sizeof(struct dxgkvmb_command_signalguestevent)) {
+               DXG_ERR("invalid signal guest event packet size");
+               return;
+       }
+       if (command->event == 0) {
+               DXG_ERR("invalid event pointer");
+               return;
+       }
+       dxgglobal_signal_host_event(command->event);
+}
+
 static void process_inband_packet(struct dxgvmbuschannel *channel,
                                  struct vmpacket_descriptor *desc)
 {
@@ -297,6 +313,7 @@ static void process_inband_packet(struct dxgvmbuschannel 
*channel,
                        switch (packet->command_type) {
                        case DXGK_VMBCOMMAND_SIGNALGUESTEVENT:
                        case DXGK_VMBCOMMAND_SIGNALGUESTEVENTPASSIVE:
+                               signal_guest_event(packet, packet_length);
                                break;
                        case DXGK_VMBCOMMAND_SENDWNFNOTIFICATION:
                                break;
@@ -959,7 +976,7 @@ dxgvmb_send_create_context(struct dxgadapter *adapter,
                                           command->priv_drv_data,
                                           args->priv_drv_data_size);
                        if (ret) {
-                               dev_err(DXGDEV,
+                               DXG_ERR(
                                        "Faled to copy private data to user");
                                ret = -EINVAL;
                                dxgvmb_send_destroy_context(adapter, process,
@@ -1706,6 +1723,206 @@ dxgvmb_send_create_sync_object(struct dxgprocess 
*process,
        return ret;
 }
 
+int dxgvmb_send_signal_sync_object(struct dxgprocess *process,
+                                  struct dxgadapter *adapter,
+                                  struct d3dddicb_signalflags flags,
+                                  u64 legacy_fence_value,
+                                  struct d3dkmthandle context,
+                                  u32 object_count,
+                                  struct d3dkmthandle __user *objects,
+                                  u32 context_count,
+                                  struct d3dkmthandle __user *contexts,
+                                  u32 fence_count,
+                                  u64 __user *fences,
+                                  struct eventfd_ctx *cpu_event_handle,
+                                  struct d3dkmthandle device)
+{
+       int ret;
+       struct dxgkvmb_command_signalsyncobject *command;
+       u32 object_size = object_count * sizeof(struct d3dkmthandle);
+       u32 context_size = context_count * sizeof(struct d3dkmthandle);
+       u32 fence_size = fences ? fence_count * sizeof(u64) : 0;
+       u8 *current_pos;
+       u32 cmd_size = sizeof(struct dxgkvmb_command_signalsyncobject) +
+           object_size + context_size + fence_size;
+       struct dxgvmbusmsg msg = {.hdr = NULL};
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       if (context.v)
+               cmd_size += sizeof(struct d3dkmthandle);
+
+       ret = init_message(&msg, adapter, process, cmd_size);
+       if (ret)
+               goto cleanup;
+       command = (void *)msg.msg;
+
+       command_vgpu_to_host_init2(&command->hdr,
+                                  DXGK_VMBCOMMAND_SIGNALSYNCOBJECT,
+                                  process->host_handle);
+
+       if (flags.enqueue_cpu_event)
+               command->cpu_event_handle = (u64) cpu_event_handle;
+       else
+               command->device = device;
+       command->flags = flags;
+       command->fence_value = legacy_fence_value;
+       command->object_count = object_count;
+       command->context_count = context_count;
+       current_pos = (u8 *) &command[1];
+       ret = copy_from_user(current_pos, objects, object_size);
+       if (ret) {
+               DXG_ERR("Failed to read objects %p %d",
+                       objects, object_size);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+       current_pos += object_size;
+       if (context.v) {
+               command->context_count++;
+               *(struct d3dkmthandle *) current_pos = context;
+               current_pos += sizeof(struct d3dkmthandle);
+       }
+       if (context_size) {
+               ret = copy_from_user(current_pos, contexts, context_size);
+               if (ret) {
+                       DXG_ERR("Failed to read contexts %p %d",
+                               contexts, context_size);
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               current_pos += context_size;
+       }
+       if (fence_size) {
+               ret = copy_from_user(current_pos, fences, fence_size);
+               if (ret) {
+                       DXG_ERR("Failed to read fences %p %d",
+                               fences, fence_size);
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+       }
+
+       if (dxgglobal->async_msg_enabled) {
+               command->hdr.async_msg = 1;
+               ret = dxgvmb_send_async_msg(msg.channel, msg.hdr, msg.size);
+       } else {
+               ret = dxgvmb_send_sync_msg_ntstatus(msg.channel, msg.hdr,
+                                                   msg.size);
+       }
+
+cleanup:
+       free_message(&msg, process);
+       if (ret)
+               DXG_TRACE("err: %d", ret);
+       return ret;
+}
+
+int dxgvmb_send_wait_sync_object_cpu(struct dxgprocess *process,
+                                    struct dxgadapter *adapter,
+                                    struct
+                                    d3dkmt_waitforsynchronizationobjectfromcpu
+                                    *args,
+                                    u64 cpu_event)
+{
+       int ret = -EINVAL;
+       struct dxgkvmb_command_waitforsyncobjectfromcpu *command;
+       u32 object_size = args->object_count * sizeof(struct d3dkmthandle);
+       u32 fence_size = args->object_count * sizeof(u64);
+       u8 *current_pos;
+       u32 cmd_size = sizeof(*command) + object_size + fence_size;
+       struct dxgvmbusmsg msg = {.hdr = NULL};
+
+       ret = init_message(&msg, adapter, process, cmd_size);
+       if (ret)
+               goto cleanup;
+       command = (void *)msg.msg;
+
+       command_vgpu_to_host_init2(&command->hdr,
+                                  DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMCPU,
+                                  process->host_handle);
+       command->device = args->device;
+       command->flags = args->flags;
+       command->object_count = args->object_count;
+       command->guest_event_pointer = (u64) cpu_event;
+       current_pos = (u8 *) &command[1];
+
+       ret = copy_from_user(current_pos, args->objects, object_size);
+       if (ret) {
+               DXG_ERR("failed to copy objects");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+       current_pos += object_size;
+       ret = copy_from_user(current_pos, args->fence_values,
+                               fence_size);
+       if (ret) {
+               DXG_ERR("failed to copy fences");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_sync_msg_ntstatus(msg.channel, msg.hdr, msg.size);
+
+cleanup:
+       free_message(&msg, process);
+       if (ret)
+               DXG_TRACE("err: %d", ret);
+       return ret;
+}
+
+int dxgvmb_send_wait_sync_object_gpu(struct dxgprocess *process,
+                                    struct dxgadapter *adapter,
+                                    struct d3dkmthandle context,
+                                    u32 object_count,
+                                    struct d3dkmthandle *objects,
+                                    u64 *fences,
+                                    bool legacy_fence)
+{
+       int ret;
+       struct dxgkvmb_command_waitforsyncobjectfromgpu *command;
+       u32 fence_size = object_count * sizeof(u64);
+       u32 object_size = object_count * sizeof(struct d3dkmthandle);
+       u8 *current_pos;
+       u32 cmd_size = object_size + fence_size - sizeof(u64) +
+           sizeof(struct dxgkvmb_command_waitforsyncobjectfromgpu);
+       struct dxgvmbusmsg msg = {.hdr = NULL};
+       struct dxgglobal *dxgglobal = dxggbl();
+
+       if (object_count == 0 || object_count > D3DDDI_MAX_OBJECT_WAITED_ON) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+       ret = init_message(&msg, adapter, process, cmd_size);
+       if (ret)
+               goto cleanup;
+       command = (void *)msg.msg;
+
+       command_vgpu_to_host_init2(&command->hdr,
+                                  DXGK_VMBCOMMAND_WAITFORSYNCOBJECTFROMGPU,
+                                  process->host_handle);
+       command->context = context;
+       command->object_count = object_count;
+       command->legacy_fence_object = legacy_fence;
+       current_pos = (u8 *) command->fence_values;
+       memcpy(current_pos, fences, fence_size);
+       current_pos += fence_size;
+       memcpy(current_pos, objects, object_size);
+
+       if (dxgglobal->async_msg_enabled) {
+               command->hdr.async_msg = 1;
+               ret = dxgvmb_send_async_msg(msg.channel, msg.hdr, msg.size);
+       } else {
+               ret = dxgvmb_send_sync_msg_ntstatus(msg.channel, msg.hdr,
+                                                   msg.size);
+       }
+
+cleanup:
+       free_message(&msg, process);
+       if (ret)
+               DXG_TRACE("err: %d", ret);
+       return ret;
+}
+
 int dxgvmb_send_query_adapter_info(struct dxgprocess *process,
                                   struct dxgadapter *adapter,
                                   struct d3dkmt_queryadapterinfo *args)
diff --git a/drivers/hv/dxgkrnl/dxgvmbus.h b/drivers/hv/dxgkrnl/dxgvmbus.h
index bbf5f31cdf81..89fecbcefbc8 100644
--- a/drivers/hv/dxgkrnl/dxgvmbus.h
+++ b/drivers/hv/dxgkrnl/dxgvmbus.h
@@ -165,6 +165,13 @@ struct dxgkvmb_command_host_to_vm {
        enum dxgkvmb_commandtype_host_to_vm     command_type;
 };
 
+struct dxgkvmb_command_signalguestevent {
+       struct dxgkvmb_command_host_to_vm hdr;
+       u64                             event;
+       u64                             process_id;
+       bool                            dereference_event;
+};
+
 /* Returns ntstatus */
 struct dxgkvmb_command_setiospaceregion {
        struct dxgkvmb_command_vm_to_host hdr;
@@ -430,4 +437,45 @@ struct dxgkvmb_command_destroysyncobject {
        struct d3dkmthandle     sync_object;
 };
 
+/* The command returns ntstatus */
+struct dxgkvmb_command_signalsyncobject {
+       struct dxgkvmb_command_vgpu_to_host hdr;
+       u32                             object_count;
+       struct d3dddicb_signalflags     flags;
+       u32                             context_count;
+       u64                             fence_value;
+       union {
+               /* Pointer to the guest event object */
+               u64                     cpu_event_handle;
+               /* Non zero when signal from CPU is done */
+               struct d3dkmthandle             device;
+       };
+       /* struct d3dkmthandle ObjectHandleArray[object_count] */
+       /* struct d3dkmthandle ContextArray[context_count]     */
+       /* u64 MonitoredFenceValueArray[object_count] */
+};
+
+/* The command returns ntstatus */
+struct dxgkvmb_command_waitforsyncobjectfromcpu {
+       struct dxgkvmb_command_vgpu_to_host hdr;
+       struct d3dkmthandle             device;
+       u32                             object_count;
+       struct d3dddi_waitforsynchronizationobjectfromcpu_flags flags;
+       u64                             guest_event_pointer;
+       bool                            dereference_event;
+       /* struct d3dkmthandle ObjectHandleArray[object_count] */
+       /* u64 FenceValueArray [object_count] */
+};
+
+/* The command returns ntstatus */
+struct dxgkvmb_command_waitforsyncobjectfromgpu {
+       struct dxgkvmb_command_vgpu_to_host hdr;
+       struct d3dkmthandle             context;
+       /* Must be 1 when bLegacyFenceObject is TRUE */
+       u32                             object_count;
+       bool                            legacy_fence_object;
+       u64                             fence_values[1];
+       /* struct d3dkmthandle ObjectHandles[object_count] */
+};
+
 #endif /* _DXGVMBUS_H */
diff --git a/drivers/hv/dxgkrnl/ioctl.c b/drivers/hv/dxgkrnl/ioctl.c
index 4bba1e209f33..0025e1ee2d4d 100644
--- a/drivers/hv/dxgkrnl/ioctl.c
+++ b/drivers/hv/dxgkrnl/ioctl.c
@@ -759,7 +759,7 @@ get_standard_alloc_priv_data(struct dxgdevice *device,
                res_priv_data = vzalloc(res_priv_data_size);
                if (res_priv_data == NULL) {
                        ret = -ENOMEM;
-                       dev_err(DXGDEV,
+                       DXG_ERR(
                                "failed to alloc memory for res priv data: %d",
                                res_priv_data_size);
                        goto cleanup;
@@ -1065,7 +1065,7 @@ dxgkio_create_allocation(struct dxgprocess *process, void 
*__user inargs)
                                        alloc_info[i].priv_drv_data,
                                        priv_data_size);
                                if (ret) {
-                                       dev_err(DXGDEV,
+                                       DXG_ERR(
                                                "failed to copy priv data");
                                        ret = -EFAULT;
                                        goto cleanup;
@@ -1348,8 +1348,10 @@ dxgkio_create_sync_object(struct dxgprocess *process, 
void *__user inargs)
        struct d3dkmt_createsynchronizationobject2 args;
        struct dxgdevice *device = NULL;
        struct dxgadapter *adapter = NULL;
+       struct eventfd_ctx *event = NULL;
        struct dxgsyncobject *syncobj = NULL;
        bool device_lock_acquired = false;
+       struct dxghosteventcpu *host_event = NULL;
 
        ret = copy_from_user(&args, inargs, sizeof(args));
        if (ret) {
@@ -1384,6 +1386,27 @@ dxgkio_create_sync_object(struct dxgprocess *process, 
void *__user inargs)
                goto cleanup;
        }
 
+       if (args.info.type == _D3DDDI_CPU_NOTIFICATION) {
+               event = eventfd_ctx_fdget((int)
+                                         args.info.cpu_notification.event);
+               if (IS_ERR(event)) {
+                       DXG_ERR("failed to reference the event");
+                       event = NULL;
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               host_event = syncobj->host_event;
+               host_event->hdr.event_id = dxgglobal_new_host_event_id();
+               host_event->cpu_event = event;
+               host_event->remove_from_list = false;
+               host_event->destroy_after_signal = false;
+               host_event->hdr.event_type = dxghostevent_cpu_event;
+               dxgglobal_add_host_event(&host_event->hdr);
+               args.info.cpu_notification.event = host_event->hdr.event_id;
+               DXG_TRACE("creating CPU notification event: %lld",
+                       args.info.cpu_notification.event);
+       }
+
        ret = dxgvmb_send_create_sync_object(process, adapter, &args, syncobj);
        if (ret < 0)
                goto cleanup;
@@ -1411,7 +1434,10 @@ dxgkio_create_sync_object(struct dxgprocess *process, 
void *__user inargs)
                        if (args.sync_object.v)
                                dxgvmb_send_destroy_sync_object(process,
                                                        args.sync_object);
+                       event = NULL;
                }
+               if (event)
+                       eventfd_ctx_put(event);
        }
        if (adapter)
                dxgadapter_release_lock_shared(adapter);
@@ -1467,6 +1493,659 @@ dxgkio_destroy_sync_object(struct dxgprocess *process, 
void *__user inargs)
        return ret;
 }
 
+static int
+dxgkio_signal_sync_object(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_signalsynchronizationobject2 args;
+       struct d3dkmt_signalsynchronizationobject2 *__user in_args = inargs;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       int ret;
+       u32 fence_count = 1;
+       struct eventfd_ctx *event = NULL;
+       struct dxghosteventcpu *host_event = NULL;
+       bool host_event_added = false;
+       u64 host_event_id = 0;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.context_count >= D3DDDI_MAX_BROADCAST_CONTEXT ||
+           args.object_count > D3DDDI_MAX_OBJECT_SIGNALED) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.flags.enqueue_cpu_event) {
+               host_event = kzalloc(sizeof(*host_event), GFP_KERNEL);
+               if (host_event == NULL) {
+                       ret = -ENOMEM;
+                       goto cleanup;
+               }
+               host_event->process = process;
+               event = eventfd_ctx_fdget((int)args.cpu_event_handle);
+               if (IS_ERR(event)) {
+                       DXG_ERR("failed to reference the event");
+                       event = NULL;
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               fence_count = 0;
+               host_event->cpu_event = event;
+               host_event_id = dxgglobal_new_host_event_id();
+               host_event->hdr.event_type = dxghostevent_cpu_event;
+               host_event->hdr.event_id = host_event_id;
+               host_event->remove_from_list = true;
+               host_event->destroy_after_signal = true;
+               dxgglobal_add_host_event(&host_event->hdr);
+               host_event_added = true;
+       }
+
+       device = dxgprocess_device_by_object_handle(process,
+                                                   HMGRENTRY_TYPE_DXGCONTEXT,
+                                                   args.context);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_signal_sync_object(process, adapter,
+                                            args.flags, args.fence.fence_value,
+                                            args.context, args.object_count,
+                                            in_args->object_array,
+                                            args.context_count,
+                                            in_args->contexts, fence_count,
+                                            NULL, (void *)host_event_id,
+                                            zerohandle);
+
+       /*
+        * When the send operation succeeds, the host event will be destroyed
+        * after signal from the host
+        */
+
+cleanup:
+
+       if (ret < 0) {
+               if (host_event_added) {
+                       /* The event might be signaled and destroyed by host */
+                       host_event = (struct dxghosteventcpu *)
+                               dxgglobal_get_host_event(host_event_id);
+                       if (host_event) {
+                               eventfd_ctx_put(event);
+                               event = NULL;
+                               kfree(host_event);
+                               host_event = NULL;
+                       }
+               }
+               if (event)
+                       eventfd_ctx_put(event);
+               if (host_event)
+                       kfree(host_event);
+       }
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_signal_sync_object_cpu(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_signalsynchronizationobjectfromcpu args;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       int ret;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+       if (args.object_count == 0 ||
+           args.object_count > D3DDDI_MAX_OBJECT_SIGNALED) {
+               DXG_TRACE("Too many syncobjects : %d", args.object_count);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       device = dxgprocess_device_by_handle(process, args.device);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_signal_sync_object(process, adapter,
+                                            args.flags, 0, zerohandle,
+                                            args.object_count, args.objects, 0,
+                                            NULL, args.object_count,
+                                            args.fence_values, NULL,
+                                            args.device);
+
+cleanup:
+
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_signal_sync_object_gpu(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_signalsynchronizationobjectfromgpu args;
+       struct d3dkmt_signalsynchronizationobjectfromgpu *__user user_args =
+           inargs;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       struct d3dddicb_signalflags flags = { };
+       int ret;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.object_count == 0 ||
+           args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       device = dxgprocess_device_by_object_handle(process,
+                                                   HMGRENTRY_TYPE_DXGCONTEXT,
+                                                   args.context);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_signal_sync_object(process, adapter,
+                                            flags, 0, zerohandle,
+                                            args.object_count,
+                                            args.objects, 1,
+                                            &user_args->context,
+                                            args.object_count,
+                                            args.monitored_fence_values, NULL,
+                                            zerohandle);
+
+cleanup:
+
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_signal_sync_object_gpu2(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_signalsynchronizationobjectfromgpu2 args;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       struct d3dkmthandle context_handle;
+       struct eventfd_ctx *event = NULL;
+       u64 *fences = NULL;
+       u32 fence_count = 0;
+       int ret;
+       struct dxghosteventcpu *host_event = NULL;
+       bool host_event_added = false;
+       u64 host_event_id = 0;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.flags.enqueue_cpu_event) {
+               if (args.object_count != 0 || args.cpu_event_handle == 0) {
+                       DXG_ERR("Bad input in EnqueueCpuEvent: %d %lld",
+                               args.object_count, args.cpu_event_handle);
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+       } else if (args.object_count == 0 ||
+                  args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE ||
+                  args.context_count == 0 ||
+                  args.context_count > DXG_MAX_VM_BUS_PACKET_SIZE) {
+               DXG_ERR("Invalid input: %d %d",
+                       args.object_count, args.context_count);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       ret = copy_from_user(&context_handle, args.contexts,
+                            sizeof(struct d3dkmthandle));
+       if (ret) {
+               DXG_ERR("failed to copy context handle");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.flags.enqueue_cpu_event) {
+               host_event = kzalloc(sizeof(*host_event), GFP_KERNEL);
+               if (host_event == NULL) {
+                       ret = -ENOMEM;
+                       goto cleanup;
+               }
+               host_event->process = process;
+               event = eventfd_ctx_fdget((int)args.cpu_event_handle);
+               if (IS_ERR(event)) {
+                       DXG_ERR("failed to reference the event");
+                       event = NULL;
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               fence_count = 0;
+               host_event->cpu_event = event;
+               host_event_id = dxgglobal_new_host_event_id();
+               host_event->hdr.event_id = host_event_id;
+               host_event->hdr.event_type = dxghostevent_cpu_event;
+               host_event->remove_from_list = true;
+               host_event->destroy_after_signal = true;
+               dxgglobal_add_host_event(&host_event->hdr);
+               host_event_added = true;
+       } else {
+               fences = args.monitored_fence_values;
+               fence_count = args.object_count;
+       }
+
+       device = dxgprocess_device_by_object_handle(process,
+                                                   HMGRENTRY_TYPE_DXGCONTEXT,
+                                                   context_handle);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_signal_sync_object(process, adapter,
+                                            args.flags, 0, zerohandle,
+                                            args.object_count, args.objects,
+                                            args.context_count, args.contexts,
+                                            fence_count, fences,
+                                            (void *)host_event_id, zerohandle);
+
+cleanup:
+
+       if (ret < 0) {
+               if (host_event_added) {
+                       /* The event might be signaled and destroyed by host */
+                       host_event = (struct dxghosteventcpu *)
+                               dxgglobal_get_host_event(host_event_id);
+                       if (host_event) {
+                               eventfd_ctx_put(event);
+                               event = NULL;
+                               kfree(host_event);
+                               host_event = NULL;
+                       }
+               }
+               if (event)
+                       eventfd_ctx_put(event);
+               if (host_event)
+                       kfree(host_event);
+       }
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_wait_sync_object(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_waitforsynchronizationobject2 args;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       int ret;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.object_count > D3DDDI_MAX_OBJECT_WAITED_ON ||
+           args.object_count == 0) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       device = dxgprocess_device_by_object_handle(process,
+                                                   HMGRENTRY_TYPE_DXGCONTEXT,
+                                                   args.context);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       DXG_TRACE("Fence value: %lld", args.fence.fence_value);
+       ret = dxgvmb_send_wait_sync_object_gpu(process, adapter,
+                                              args.context, args.object_count,
+                                              args.object_array,
+                                              &args.fence.fence_value, true);
+
+cleanup:
+
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_wait_sync_object_cpu(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_waitforsynchronizationobjectfromcpu args;
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       struct eventfd_ctx *event = NULL;
+       struct dxghosteventcpu host_event = { };
+       struct dxghosteventcpu *async_host_event = NULL;
+       struct completion local_event = { };
+       u64 event_id = 0;
+       int ret;
+       bool host_event_added = false;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE ||
+           args.object_count == 0) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.async_event) {
+               async_host_event = kzalloc(sizeof(*async_host_event),
+                                       GFP_KERNEL);
+               if (async_host_event == NULL) {
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               async_host_event->process = process;
+               event = eventfd_ctx_fdget((int)args.async_event);
+               if (IS_ERR(event)) {
+                       DXG_ERR("failed to reference the event");
+                       event = NULL;
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+               async_host_event->cpu_event = event;
+               async_host_event->hdr.event_id = dxgglobal_new_host_event_id();
+               async_host_event->destroy_after_signal = true;
+               async_host_event->hdr.event_type = dxghostevent_cpu_event;
+               dxgglobal_add_host_event(&async_host_event->hdr);
+               event_id = async_host_event->hdr.event_id;
+               host_event_added = true;
+       } else {
+               init_completion(&local_event);
+               host_event.completion_event = &local_event;
+               host_event.hdr.event_id = dxgglobal_new_host_event_id();
+               host_event.hdr.event_type = dxghostevent_cpu_event;
+               dxgglobal_add_host_event(&host_event.hdr);
+               event_id = host_event.hdr.event_id;
+       }
+
+       device = dxgprocess_device_by_handle(process, args.device);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_wait_sync_object_cpu(process, adapter,
+                                              &args, event_id);
+       if (ret < 0)
+               goto cleanup;
+
+       if (args.async_event == 0) {
+               dxgadapter_release_lock_shared(adapter);
+               adapter = NULL;
+               ret = wait_for_completion_interruptible(&local_event);
+               if (ret) {
+                       DXG_ERR("wait_completion_interruptible: %d",
+                               ret);
+                       ret = -ERESTARTSYS;
+               }
+       }
+
+cleanup:
+
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+       if (host_event.hdr.event_id)
+               dxgglobal_remove_host_event(&host_event.hdr);
+       if (ret < 0) {
+               if (host_event_added) {
+                       async_host_event = (struct dxghosteventcpu *)
+                               dxgglobal_get_host_event(event_id);
+                       if (async_host_event) {
+                               if (async_host_event->hdr.event_type ==
+                                   dxghostevent_cpu_event) {
+                                       eventfd_ctx_put(event);
+                                       event = NULL;
+                                       kfree(async_host_event);
+                                       async_host_event = NULL;
+                               } else {
+                                       DXG_ERR("Invalid event type");
+                                       DXGKRNL_ASSERT(0);
+                               }
+                       }
+               }
+               if (event)
+                       eventfd_ctx_put(event);
+               if (async_host_event)
+                       kfree(async_host_event);
+       }
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
+static int
+dxgkio_wait_sync_object_gpu(struct dxgprocess *process, void *__user inargs)
+{
+       struct d3dkmt_waitforsynchronizationobjectfromgpu args;
+       struct dxgcontext *context = NULL;
+       struct d3dkmthandle device_handle = {};
+       struct dxgdevice *device = NULL;
+       struct dxgadapter *adapter = NULL;
+       struct dxgsyncobject *syncobj = NULL;
+       struct d3dkmthandle *objects = NULL;
+       u32 object_size;
+       u64 *fences = NULL;
+       int ret;
+       enum hmgrentry_type syncobj_type = HMGRENTRY_TYPE_FREE;
+       bool monitored_fence = false;
+
+       ret = copy_from_user(&args, inargs, sizeof(args));
+       if (ret) {
+               DXG_ERR("failed to copy input args");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       if (args.object_count > DXG_MAX_VM_BUS_PACKET_SIZE ||
+           args.object_count == 0) {
+               DXG_ERR("Invalid object count: %d", args.object_count);
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       object_size = sizeof(struct d3dkmthandle) * args.object_count;
+       objects = vzalloc(object_size);
+       if (objects == NULL) {
+               ret = -ENOMEM;
+               goto cleanup;
+       }
+       ret = copy_from_user(objects, args.objects, object_size);
+       if (ret) {
+               DXG_ERR("failed to copy objects");
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       hmgrtable_lock(&process->handle_table, DXGLOCK_SHARED);
+       context = hmgrtable_get_object_by_type(&process->handle_table,
+                                              HMGRENTRY_TYPE_DXGCONTEXT,
+                                              args.context);
+       if (context) {
+               device_handle = context->device_handle;
+               syncobj_type =
+                   hmgrtable_get_object_type(&process->handle_table,
+                                             objects[0]);
+       }
+       if (device_handle.v == 0) {
+               DXG_ERR("Invalid context handle: %x", args.context.v);
+               ret = -EINVAL;
+       } else {
+               if (syncobj_type == HMGRENTRY_TYPE_MONITOREDFENCE) {
+                       monitored_fence = true;
+               } else if (syncobj_type == HMGRENTRY_TYPE_DXGSYNCOBJECT) {
+                       syncobj =
+                           hmgrtable_get_object_by_type(&process->handle_table,
+                                               HMGRENTRY_TYPE_DXGSYNCOBJECT,
+                                               objects[0]);
+                       if (syncobj == NULL) {
+                               DXG_ERR("Invalid syncobj: %x",
+                                       objects[0].v);
+                               ret = -EINVAL;
+                       } else {
+                               monitored_fence = syncobj->monitored_fence;
+                       }
+               } else {
+                       DXG_ERR("Invalid syncobj type: %x",
+                               objects[0].v);
+                       ret = -EINVAL;
+               }
+       }
+       hmgrtable_unlock(&process->handle_table, DXGLOCK_SHARED);
+
+       if (ret < 0)
+               goto cleanup;
+
+       if (monitored_fence) {
+               object_size = sizeof(u64) * args.object_count;
+               fences = vzalloc(object_size);
+               if (fences == NULL) {
+                       ret = -ENOMEM;
+                       goto cleanup;
+               }
+               ret = copy_from_user(fences, args.monitored_fence_values,
+                                    object_size);
+               if (ret) {
+                       DXG_ERR("failed to copy fences");
+                       ret = -EINVAL;
+                       goto cleanup;
+               }
+       } else {
+               fences = &args.fence_value;
+       }
+
+       device = dxgprocess_device_by_handle(process, device_handle);
+       if (device == NULL) {
+               ret = -EINVAL;
+               goto cleanup;
+       }
+
+       adapter = device->adapter;
+       ret = dxgadapter_acquire_lock_shared(adapter);
+       if (ret < 0) {
+               adapter = NULL;
+               goto cleanup;
+       }
+
+       ret = dxgvmb_send_wait_sync_object_gpu(process, adapter,
+                                              args.context, args.object_count,
+                                              objects, fences,
+                                              !monitored_fence);
+
+cleanup:
+
+       if (adapter)
+               dxgadapter_release_lock_shared(adapter);
+       if (device)
+               kref_put(&device->device_kref, dxgdevice_release);
+       if (objects)
+               vfree(objects);
+       if (fences && fences != &args.fence_value)
+               vfree(fences);
+
+       DXG_TRACE("ioctl:%s %d", errorstr(ret), ret);
+       return ret;
+}
+
 static struct ioctl_desc ioctls[] = {
 /* 0x00 */     {},
 /* 0x01 */     {dxgkio_open_adapter_from_luid, LX_DXOPENADAPTERFROMLUID},
@@ -1485,8 +2164,8 @@ static struct ioctl_desc ioctls[] = {
 /* 0x0e */     {},
 /* 0x0f */     {},
 /* 0x10 */     {dxgkio_create_sync_object, LX_DXCREATESYNCHRONIZATIONOBJECT},
-/* 0x11 */     {},
-/* 0x12 */     {},
+/* 0x11 */     {dxgkio_signal_sync_object, LX_DXSIGNALSYNCHRONIZATIONOBJECT},
+/* 0x12 */     {dxgkio_wait_sync_object, LX_DXWAITFORSYNCHRONIZATIONOBJECT},
 /* 0x13 */     {dxgkio_destroy_allocation, LX_DXDESTROYALLOCATION2},
 /* 0x14 */     {dxgkio_enum_adapters, LX_DXENUMADAPTERS2},
 /* 0x15 */     {dxgkio_close_adapter, LX_DXCLOSEADAPTER},
@@ -1517,17 +2196,22 @@ static struct ioctl_desc ioctls[] = {
 /* 0x2e */     {},
 /* 0x2f */     {},
 /* 0x30 */     {},
-/* 0x31 */     {},
-/* 0x32 */     {},
-/* 0x33 */     {},
+/* 0x31 */     {dxgkio_signal_sync_object_cpu,
+                LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU},
+/* 0x32 */     {dxgkio_signal_sync_object_gpu,
+                LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU},
+/* 0x33 */     {dxgkio_signal_sync_object_gpu2,
+                LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2},
 /* 0x34 */     {},
 /* 0x35 */     {},
 /* 0x36 */     {},
 /* 0x37 */     {},
 /* 0x38 */     {},
 /* 0x39 */     {},
-/* 0x3a */     {},
-/* 0x3b */     {},
+/* 0x3a */     {dxgkio_wait_sync_object_cpu,
+                LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMCPU},
+/* 0x3b */     {dxgkio_wait_sync_object_gpu,
+                LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU},
 /* 0x3c */     {},
 /* 0x3d */     {},
 /* 0x3e */     {dxgkio_enum_adapters3, LX_DXENUMADAPTERS3},
diff --git a/drivers/hv/dxgkrnl/misc.h b/drivers/hv/dxgkrnl/misc.h
index a51b29a6a68f..ee2ebfdd1c13 100644
--- a/drivers/hv/dxgkrnl/misc.h
+++ b/drivers/hv/dxgkrnl/misc.h
@@ -25,6 +25,8 @@ extern const struct d3dkmthandle zerohandle;
  * The locks here are in the order from lowest to highest.
  * When a lower lock is held, the higher lock should not be acquired.
  *
+ * device_list_mutex
+ * host_event_list_mutex
  * channel_lock (VMBus channel lock)
  * fd_mutex
  * plistmutex (process list mutex)
diff --git a/include/uapi/misc/d3dkmthk.h b/include/uapi/misc/d3dkmthk.h
index 4e1069f41d76..39055b0c1069 100644
--- a/include/uapi/misc/d3dkmthk.h
+++ b/include/uapi/misc/d3dkmthk.h
@@ -60,6 +60,9 @@ struct winluid {
 
 #define D3DKMT_CREATEALLOCATION_MAX            1024
 #define D3DKMT_ADAPTERS_MAX                    64
+#define D3DDDI_MAX_BROADCAST_CONTEXT           64
+#define D3DDDI_MAX_OBJECT_WAITED_ON            32
+#define D3DDDI_MAX_OBJECT_SIGNALED             32
 
 struct d3dkmt_adapterinfo {
        struct d3dkmthandle             adapter_handle;
@@ -343,6 +346,148 @@ struct d3dkmt_createsynchronizationobject2 {
        __u32                                           reserved1;
 };
 
+struct d3dkmt_waitforsynchronizationobject2 {
+       struct d3dkmthandle     context;
+       __u32                   object_count;
+       struct d3dkmthandle     object_array[D3DDDI_MAX_OBJECT_WAITED_ON];
+       union {
+               struct {
+                       __u64   fence_value;
+               } fence;
+               __u64           reserved[8];
+       };
+};
+
+struct d3dddicb_signalflags {
+       union {
+               struct {
+                       __u32   signal_at_submission:1;
+                       __u32   enqueue_cpu_event:1;
+                       __u32   allow_fence_rewind:1;
+                       __u32   reserved:28;
+                       __u32   DXGK_SIGNAL_FLAG_INTERNAL0:1;
+               };
+               __u32           value;
+       };
+};
+
+struct d3dkmt_signalsynchronizationobject2 {
+       struct d3dkmthandle             context;
+       __u32                           object_count;
+       struct d3dkmthandle     object_array[D3DDDI_MAX_OBJECT_SIGNALED];
+       struct d3dddicb_signalflags     flags;
+       __u32                           context_count;
+       struct d3dkmthandle             contexts[D3DDDI_MAX_BROADCAST_CONTEXT];
+       union {
+               struct {
+                       __u64           fence_value;
+               } fence;
+               __u64                   cpu_event_handle;
+               __u64                   reserved[8];
+       };
+};
+
+struct d3dddi_waitforsynchronizationobjectfromcpu_flags {
+       union {
+               struct {
+                       __u32   wait_any:1;
+                       __u32   reserved:31;
+               };
+               __u32           value;
+       };
+};
+
+struct d3dkmt_waitforsynchronizationobjectfromcpu {
+       struct d3dkmthandle     device;
+       __u32                   object_count;
+#ifdef __KERNEL__
+       struct d3dkmthandle     *objects;
+       __u64                   *fence_values;
+#else
+       __u64                   objects;
+       __u64                   fence_values;
+#endif
+       __u64                   async_event;
+       struct d3dddi_waitforsynchronizationobjectfromcpu_flags flags;
+};
+
+struct d3dkmt_signalsynchronizationobjectfromcpu {
+       struct d3dkmthandle     device;
+       __u32                   object_count;
+#ifdef __KERNEL__
+       struct d3dkmthandle     *objects;
+       __u64                   *fence_values;
+#else
+       __u64                   objects;
+       __u64                   fence_values;
+#endif
+       struct d3dddicb_signalflags     flags;
+};
+
+struct d3dkmt_waitforsynchronizationobjectfromgpu {
+       struct d3dkmthandle     context;
+       __u32                   object_count;
+#ifdef __KERNEL__
+       struct d3dkmthandle     *objects;
+#else
+       __u64                   objects;
+#endif
+       union {
+#ifdef __KERNEL__
+               __u64           *monitored_fence_values;
+#else
+               __u64           monitored_fence_values;
+#endif
+               __u64           fence_value;
+               __u64           reserved[8];
+       };
+};
+
+struct d3dkmt_signalsynchronizationobjectfromgpu {
+       struct d3dkmthandle     context;
+       __u32                   object_count;
+#ifdef __KERNEL__
+       struct d3dkmthandle     *objects;
+#else
+       __u64                   objects;
+#endif
+       union {
+#ifdef __KERNEL__
+               __u64           *monitored_fence_values;
+#else
+               __u64           monitored_fence_values;
+#endif
+               __u64           reserved[8];
+       };
+};
+
+struct d3dkmt_signalsynchronizationobjectfromgpu2 {
+       __u32                           object_count;
+       __u32                           reserved1;
+#ifdef __KERNEL__
+       struct d3dkmthandle             *objects;
+#else
+       __u64                           objects;
+#endif
+       struct d3dddicb_signalflags     flags;
+       __u32                           context_count;
+#ifdef __KERNEL__
+       struct d3dkmthandle             *contexts;
+#else
+       __u64                           contexts;
+#endif
+       union {
+               __u64                   fence_value;
+               __u64                   cpu_event_handle;
+#ifdef __KERNEL__
+               __u64                   *monitored_fence_values;
+#else
+               __u64                   monitored_fence_values;
+#endif
+               __u64                   reserved[8];
+       };
+};
+
 struct d3dkmt_destroysynchronizationobject {
        struct d3dkmthandle     sync_object;
 };
@@ -576,6 +721,10 @@ struct d3dkmt_enumadapters3 {
        _IOWR(0x47, 0x09, struct d3dkmt_queryadapterinfo)
 #define LX_DXCREATESYNCHRONIZATIONOBJECT \
        _IOWR(0x47, 0x10, struct d3dkmt_createsynchronizationobject2)
+#define LX_DXSIGNALSYNCHRONIZATIONOBJECT \
+       _IOWR(0x47, 0x11, struct d3dkmt_signalsynchronizationobject2)
+#define LX_DXWAITFORSYNCHRONIZATIONOBJECT \
+       _IOWR(0x47, 0x12, struct d3dkmt_waitforsynchronizationobject2)
 #define LX_DXDESTROYALLOCATION2                \
        _IOWR(0x47, 0x13, struct d3dkmt_destroyallocation2)
 #define LX_DXENUMADAPTERS2             \
@@ -586,6 +735,16 @@ struct d3dkmt_enumadapters3 {
        _IOWR(0x47, 0x19, struct d3dkmt_destroydevice)
 #define LX_DXDESTROYSYNCHRONIZATIONOBJECT \
        _IOWR(0x47, 0x1d, struct d3dkmt_destroysynchronizationobject)
+#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMCPU \
+       _IOWR(0x47, 0x31, struct d3dkmt_signalsynchronizationobjectfromcpu)
+#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU \
+       _IOWR(0x47, 0x32, struct d3dkmt_signalsynchronizationobjectfromgpu)
+#define LX_DXSIGNALSYNCHRONIZATIONOBJECTFROMGPU2 \
+       _IOWR(0x47, 0x33, struct d3dkmt_signalsynchronizationobjectfromgpu2)
+#define LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMCPU \
+       _IOWR(0x47, 0x3a, struct d3dkmt_waitforsynchronizationobjectfromcpu)
+#define LX_DXWAITFORSYNCHRONIZATIONOBJECTFROMGPU \
+       _IOWR(0x47, 0x3b, struct d3dkmt_waitforsynchronizationobjectfromgpu)
 #define LX_DXENUMADAPTERS3             \
        _IOWR(0x47, 0x3e, struct d3dkmt_enumadapters3)
 

Reply via email to