[i-g-t,1/8] lib/igt_dummyload: add igt_cork
diff mbox

Message ID 1507847254-3289-2-git-send-email-daniele.ceraolospurio@intel.com
State New
Headers show

Commit Message

Daniele Ceraolo Spurio Oct. 12, 2017, 10:27 p.m. UTC
The "cork" bo (imported bo with attached fence) is used in several
tests to stall execution. Moving it to a common place makes the codebase
cleaner.

Note that the actual test updates is done in follow up patches as it is
simpler to do in one go after one more common function is added in the
next patch.

Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
---
 lib/igt_dummyload.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_dummyload.h | 11 ++++++++
 2 files changed, 86 insertions(+)

Comments

Chris Wilson Oct. 12, 2017, 10:48 p.m. UTC | #1
Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
> index 215425f..d20a867 100644
> --- a/lib/igt_dummyload.h
> +++ b/lib/igt_dummyload.h

<insert ambition for killing off using igt for i915 specific constructs>

> @@ -29,6 +29,7 @@
>  #include <time.h>
>  
>  #include "igt_aux.h"
> +#include "igt_vgem.h"
>  
>  typedef struct igt_spin {
>         unsigned int handle;
> @@ -51,4 +52,14 @@ void igt_spin_batch_free(int fd, igt_spin_t *spin);
>  
>  void igt_terminate_spin_batches(void);
>  
> +typedef struct igt_cork {
> +       int device;
> +       uint32_t handle;
> +       uint32_t fence;
> +} igt_cork_t;

Nothing here depends on igt_vgem.h, that should just be for
igt_dummyload.c

> +
> +igt_cork_t *igt_cork_new(int fd);
> +void igt_cork_signal(igt_cork_t *cork);
> +void igt_cork_free(int fd, igt_cork_t *cork);
> +
>  #endif /* __IGT_DUMMYLOAD_H__ */
Chris Wilson Oct. 12, 2017, 10:55 p.m. UTC | #2
Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> +typedef struct igt_cork {
> +       int device;
> +       uint32_t handle;
> +       uint32_t fence;
> +} igt_cork_t;

Oh, just struct no need for typedef when you are transparent.
-Chris
Chris Wilson Oct. 12, 2017, 10:57 p.m. UTC | #3
Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> +igt_cork_t *igt_cork_new(int fd);

_new does not imply plugged.

> +void igt_cork_signal(igt_cork_t *cork);

When have you signaled a cork?

> +void igt_cork_free(int fd, igt_cork_t *cork);

_free does not imply unplug.
-Chris
Chris Wilson Oct. 13, 2017, 8:31 a.m. UTC | #4
Quoting Chris Wilson (2017-10-12 23:57:38)
> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> > +igt_cork_t *igt_cork_new(int fd);
> 
> _new does not imply plugged.
> 
> > +void igt_cork_signal(igt_cork_t *cork);
> 
> When have you signaled a cork?
> 
> > +void igt_cork_free(int fd, igt_cork_t *cork);
> 
> _free does not imply unplug.

To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
reference to TCP_CORK which does the same thing, but plug/unplug are
more commonplace (at least in kernel code).

I don't see any reason why we need a malloc here.
-Chris
Daniele Ceraolo Spurio Oct. 13, 2017, 4:37 p.m. UTC | #5
On 13/10/17 01:31, Chris Wilson wrote:
> Quoting Chris Wilson (2017-10-12 23:57:38)
>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
>>> +igt_cork_t *igt_cork_new(int fd);
>>
>> _new does not imply plugged.
>>
>>> +void igt_cork_signal(igt_cork_t *cork);
>>
>> When have you signaled a cork?
>>
>>> +void igt_cork_free(int fd, igt_cork_t *cork);
>>
>> _free does not imply unplug.
> 
> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
> reference to TCP_CORK which does the same thing, but plug/unplug are
> more commonplace (at least in kernel code).
> 
> I don't see any reason why we need a malloc here.
> -Chris
> 

I added the malloc just to use the same approach as the spin_batch, I'll 
get rid of it.
My concern with the existing plug/unplug scheme was that the plug() 
function in the various tests didn't really plug anything but just 
created the bo and that was slightly confusing.
What do you think of going with:

	struct igt_cork {
		int device;
		uint32_t handle;
		uint32_t fence;
	};

	struct igt_cork igt_cork_create(int fd);
	void igt_cork_unplug(struct igt_cork *cork);
	void igt_cork_close(int fd, struct igt_cork *cork);
	void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);

The plug() function is still missing, as we do the actual plugging by 
adding the object to the execbuf and I don't think that would be clean 
to wrap in the library. I thought of adding something like 
"get_plugging_handle()" to return cork->handle and make it more explicit 
that that handle was responsible for the plugging but it seemed a bit 
overkill.

Daniele
Daniele Ceraolo Spurio Oct. 18, 2017, 3:49 p.m. UTC | #6
On 13/10/17 09:37, Daniele Ceraolo Spurio wrote:
> 
> 
> On 13/10/17 01:31, Chris Wilson wrote:
>> Quoting Chris Wilson (2017-10-12 23:57:38)
>>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
>>>> +igt_cork_t *igt_cork_new(int fd);
>>>
>>> _new does not imply plugged.
>>>
>>>> +void igt_cork_signal(igt_cork_t *cork);
>>>
>>> When have you signaled a cork?
>>>
>>>> +void igt_cork_free(int fd, igt_cork_t *cork);
>>>
>>> _free does not imply unplug.
>>
>> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
>> reference to TCP_CORK which does the same thing, but plug/unplug are
>> more commonplace (at least in kernel code).
>>
>> I don't see any reason why we need a malloc here.
>> -Chris
>>
> 
> I added the malloc just to use the same approach as the spin_batch, I'll 
> get rid of it.
> My concern with the existing plug/unplug scheme was that the plug() 
> function in the various tests didn't really plug anything but just 
> created the bo and that was slightly confusing.
> What do you think of going with:
> 
>      struct igt_cork {
>          int device;
>          uint32_t handle;
>          uint32_t fence;
>      };
> 
>      struct igt_cork igt_cork_create(int fd);
>      void igt_cork_unplug(struct igt_cork *cork);
>      void igt_cork_close(int fd, struct igt_cork *cork);
>      void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);
> 
> The plug() function is still missing, as we do the actual plugging by 
> adding the object to the execbuf and I don't think that would be clean 
> to wrap in the library. I thought of adding something like 
> "get_plugging_handle()" to return cork->handle and make it more explicit 
> that that handle was responsible for the plugging but it seemed a bit 
> overkill.
> 
> Daniele
>

Hi Chris,

any feedback on this?

Thanks,
Daniele
Chris Wilson Oct. 18, 2017, 4:04 p.m. UTC | #7
Quoting Daniele Ceraolo Spurio (2017-10-18 16:49:24)
> 
> 
> On 13/10/17 09:37, Daniele Ceraolo Spurio wrote:
> > 
> > 
> > On 13/10/17 01:31, Chris Wilson wrote:
> >> Quoting Chris Wilson (2017-10-12 23:57:38)
> >>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> >>>> +igt_cork_t *igt_cork_new(int fd);
> >>>
> >>> _new does not imply plugged.
> >>>
> >>>> +void igt_cork_signal(igt_cork_t *cork);
> >>>
> >>> When have you signaled a cork?
> >>>
> >>>> +void igt_cork_free(int fd, igt_cork_t *cork);
> >>>
> >>> _free does not imply unplug.
> >>
> >> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
> >> reference to TCP_CORK which does the same thing, but plug/unplug are
> >> more commonplace (at least in kernel code).
> >>
> >> I don't see any reason why we need a malloc here.
> >> -Chris
> >>
> > 
> > I added the malloc just to use the same approach as the spin_batch, I'll 
> > get rid of it.
> > My concern with the existing plug/unplug scheme was that the plug() 
> > function in the various tests didn't really plug anything but just 
> > created the bo and that was slightly confusing.

It created a bo with an unsignaled fence, that's enough to plug anything
attached to it. Since we can't just say plug(device) we have to say
execbuf(device, plug()).

> > What do you think of going with:
> > 
> >      struct igt_cork {
> >          int device;
> >          uint32_t handle;
> >          uint32_t fence;
> >      };
> > 
> >      struct igt_cork igt_cork_create(int fd);
> >      void igt_cork_unplug(struct igt_cork *cork);
> >      void igt_cork_close(int fd, struct igt_cork *cork);
> >      void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);

close will always be unplug; there's no differentiation, in both APIs we
ensure that any fence associated with the device or timeline fd is
signaled upon release. We could lose the fence and still work, but for
us it gives us the means by which we can do a test-and-set and report an
issue where the fence was signaled too early (due to slow test setup).
Similarly once unplugged, there is no use for the struct anymore, you
could release the device/timeline, but we've embedded it because in
terms of overhead, so far it has been insignificant.

Leaving a fence dangling by separating unplug/close is a good way to
leave lots of timeouts and GPU resets behind.
-Chris
Daniele Ceraolo Spurio Oct. 18, 2017, 4:50 p.m. UTC | #8
On 18/10/17 09:04, Chris Wilson wrote:
> Quoting Daniele Ceraolo Spurio (2017-10-18 16:49:24)
>>
>>
>> On 13/10/17 09:37, Daniele Ceraolo Spurio wrote:
>>>
>>>
>>> On 13/10/17 01:31, Chris Wilson wrote:
>>>> Quoting Chris Wilson (2017-10-12 23:57:38)
>>>>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
>>>>>> +igt_cork_t *igt_cork_new(int fd);
>>>>>
>>>>> _new does not imply plugged.
>>>>>
>>>>>> +void igt_cork_signal(igt_cork_t *cork);
>>>>>
>>>>> When have you signaled a cork?
>>>>>
>>>>>> +void igt_cork_free(int fd, igt_cork_t *cork);
>>>>>
>>>>> _free does not imply unplug.
>>>>
>>>> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
>>>> reference to TCP_CORK which does the same thing, but plug/unplug are
>>>> more commonplace (at least in kernel code).
>>>>
>>>> I don't see any reason why we need a malloc here.
>>>> -Chris
>>>>
>>>
>>> I added the malloc just to use the same approach as the spin_batch, I'll
>>> get rid of it.
>>> My concern with the existing plug/unplug scheme was that the plug()
>>> function in the various tests didn't really plug anything but just
>>> created the bo and that was slightly confusing.
> 
> It created a bo with an unsignaled fence, that's enough to plug anything
> attached to it. Since we can't just say plug(device) we have to say
> execbuf(device, plug()).
> 
>>> What do you think of going with:
>>>
>>>       struct igt_cork {
>>>           int device;
>>>           uint32_t handle;
>>>           uint32_t fence;
>>>       };
>>>
>>>       struct igt_cork igt_cork_create(int fd);
>>>       void igt_cork_unplug(struct igt_cork *cork);
>>>       void igt_cork_close(int fd, struct igt_cork *cork);
>>>       void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);
> 
> close will always be unplug; there's no differentiation, in both APIs we
> ensure that any fence associated with the device or timeline fd is
> signaled upon release. We could lose the fence and still work, but for
> us it gives us the means by which we can do a test-and-set and report an
> issue where the fence was signaled too early (due to slow test setup).
> Similarly once unplugged, there is no use for the struct anymore, you
> could release the device/timeline, but we've embedded it because in
> terms of overhead, so far it has been insignificant.
> 
> Leaving a fence dangling by separating unplug/close is a good way to
> leave lots of timeouts and GPU resets behind.
> -Chris
> 

What I wanted to separate is the unplugging from the closing of the BO 
handle, because in some case we keep the BO around for a while after 
unblocking the execution. In most of those cases the BO handle is not 
currently closed, but it seemed dirty to me to keep that behavior and 
have library functions that didn't clean up after themselves. Would it 
be ok for you to keep both unplug and close, with the first doing just 
the signaling of the fence and the second doing both the signaling (if 
not already done) and the bo handle closure? this would basically be 
what is in the current version of the patches but with a different name. 
Alternatively, we could explicitly return the handle from the plug() 
call to make it clearer that it needs closing:

uint32_t igt_plug(int fd, struct igt_cork *cork);
void igt_unplug(struct igt_cork *cork);

Could also keep the typedef in this case since the handle is returned 
and we don't need to peek inside the cork struct.

Thanks,
Daniele
Chris Wilson Oct. 19, 2017, 6:12 p.m. UTC | #9
Quoting Daniele Ceraolo Spurio (2017-10-18 17:50:33)
> 
> 
> On 18/10/17 09:04, Chris Wilson wrote:
> > Quoting Daniele Ceraolo Spurio (2017-10-18 16:49:24)
> >>
> >>
> >> On 13/10/17 09:37, Daniele Ceraolo Spurio wrote:
> >>>
> >>>
> >>> On 13/10/17 01:31, Chris Wilson wrote:
> >>>> Quoting Chris Wilson (2017-10-12 23:57:38)
> >>>>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
> >>>>>> +igt_cork_t *igt_cork_new(int fd);
> >>>>>
> >>>>> _new does not imply plugged.
> >>>>>
> >>>>>> +void igt_cork_signal(igt_cork_t *cork);
> >>>>>
> >>>>> When have you signaled a cork?
> >>>>>
> >>>>>> +void igt_cork_free(int fd, igt_cork_t *cork);
> >>>>>
> >>>>> _free does not imply unplug.
> >>>>
> >>>> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
> >>>> reference to TCP_CORK which does the same thing, but plug/unplug are
> >>>> more commonplace (at least in kernel code).
> >>>>
> >>>> I don't see any reason why we need a malloc here.
> >>>> -Chris
> >>>>
> >>>
> >>> I added the malloc just to use the same approach as the spin_batch, I'll
> >>> get rid of it.
> >>> My concern with the existing plug/unplug scheme was that the plug()
> >>> function in the various tests didn't really plug anything but just
> >>> created the bo and that was slightly confusing.
> > 
> > It created a bo with an unsignaled fence, that's enough to plug anything
> > attached to it. Since we can't just say plug(device) we have to say
> > execbuf(device, plug()).
> > 
> >>> What do you think of going with:
> >>>
> >>>       struct igt_cork {
> >>>           int device;
> >>>           uint32_t handle;
> >>>           uint32_t fence;
> >>>       };
> >>>
> >>>       struct igt_cork igt_cork_create(int fd);
> >>>       void igt_cork_unplug(struct igt_cork *cork);
> >>>       void igt_cork_close(int fd, struct igt_cork *cork);
> >>>       void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);
> > 
> > close will always be unplug; there's no differentiation, in both APIs we
> > ensure that any fence associated with the device or timeline fd is
> > signaled upon release. We could lose the fence and still work, but for
> > us it gives us the means by which we can do a test-and-set and report an
> > issue where the fence was signaled too early (due to slow test setup).
> > Similarly once unplugged, there is no use for the struct anymore, you
> > could release the device/timeline, but we've embedded it because in
> > terms of overhead, so far it has been insignificant.
> > 
> > Leaving a fence dangling by separating unplug/close is a good way to
> > leave lots of timeouts and GPU resets behind.
> > -Chris
> > 
> 
> What I wanted to separate is the unplugging from the closing of the BO 
> handle, because in some case we keep the BO around for a while after 
> unblocking the execution.

Where? What value could it possibly have? You know the state of its
fence, so presumably you want the contents. In such a situation you don't
need a dummy cork to plug the queue, you have a real object with which
you want interact.

> In most of those cases the BO handle is not 
> currently closed,

Every single unplug() function is closing the device; which closes the
handle; gem_close() is superfluous. Early on I kept the vgem fd around
and just needed to close the handle, but it looks like all of those
functions have now been converted to own their device.
-Chris
Daniele Ceraolo Spurio Oct. 19, 2017, 8:15 p.m. UTC | #10
On 19/10/17 11:12, Chris Wilson wrote:
> Quoting Daniele Ceraolo Spurio (2017-10-18 17:50:33)
>>
>>
>> On 18/10/17 09:04, Chris Wilson wrote:
>>> Quoting Daniele Ceraolo Spurio (2017-10-18 16:49:24)
>>>>
>>>>
>>>> On 13/10/17 09:37, Daniele Ceraolo Spurio wrote:
>>>>>
>>>>>
>>>>> On 13/10/17 01:31, Chris Wilson wrote:
>>>>>> Quoting Chris Wilson (2017-10-12 23:57:38)
>>>>>>> Quoting Daniele Ceraolo Spurio (2017-10-12 23:27:27)
>>>>>>>> +igt_cork_t *igt_cork_new(int fd);
>>>>>>>
>>>>>>> _new does not imply plugged.
>>>>>>>
>>>>>>>> +void igt_cork_signal(igt_cork_t *cork);
>>>>>>>
>>>>>>> When have you signaled a cork?
>>>>>>>
>>>>>>>> +void igt_cork_free(int fd, igt_cork_t *cork);
>>>>>>>
>>>>>>> _free does not imply unplug.
>>>>>>
>>>>>> To be clear the verbs are to plug and unplug a queue/schedule. Cork is a
>>>>>> reference to TCP_CORK which does the same thing, but plug/unplug are
>>>>>> more commonplace (at least in kernel code).
>>>>>>
>>>>>> I don't see any reason why we need a malloc here.
>>>>>> -Chris
>>>>>>
>>>>>
>>>>> I added the malloc just to use the same approach as the spin_batch, I'll
>>>>> get rid of it.
>>>>> My concern with the existing plug/unplug scheme was that the plug()
>>>>> function in the various tests didn't really plug anything but just
>>>>> created the bo and that was slightly confusing.
>>>
>>> It created a bo with an unsignaled fence, that's enough to plug anything
>>> attached to it. Since we can't just say plug(device) we have to say
>>> execbuf(device, plug()).
>>>
>>>>> What do you think of going with:
>>>>>
>>>>>        struct igt_cork {
>>>>>            int device;
>>>>>            uint32_t handle;
>>>>>            uint32_t fence;
>>>>>        };
>>>>>
>>>>>        struct igt_cork igt_cork_create(int fd);
>>>>>        void igt_cork_unplug(struct igt_cork *cork);
>>>>>        void igt_cork_close(int fd, struct igt_cork *cork);
>>>>>        void igt_cork_unplug_and_close(int fd, struct igt_cork *cork);
>>>
>>> close will always be unplug; there's no differentiation, in both APIs we
>>> ensure that any fence associated with the device or timeline fd is
>>> signaled upon release. We could lose the fence and still work, but for
>>> us it gives us the means by which we can do a test-and-set and report an
>>> issue where the fence was signaled too early (due to slow test setup).
>>> Similarly once unplugged, there is no use for the struct anymore, you
>>> could release the device/timeline, but we've embedded it because in
>>> terms of overhead, so far it has been insignificant.
>>>
>>> Leaving a fence dangling by separating unplug/close is a good way to
>>> leave lots of timeouts and GPU resets behind.
>>> -Chris
>>>
>>
>> What I wanted to separate is the unplugging from the closing of the BO
>> handle, because in some case we keep the BO around for a while after
>> unblocking the execution.
> 
> Where? What value could it possibly have? You know the state of its
> fence, so presumably you want the contents. In such a situation you don't
> need a dummy cork to plug the queue, you have a real object with which
> you want interact.
> 
>> In most of those cases the BO handle is not
>> currently closed,
> 
> Every single unplug() function is closing the device; which closes the
> handle; gem_close() is superfluous. Early on I kept the vgem fd around
> and just needed to close the handle, but it looks like all of those
> functions have now been converted to own their device.
> -Chris
> 

Apologies for being unclear, what I was referring to was the imported BO 
handle, not the one from the vgem device. In a couple of tests 
(gem_wait, gem_exec_latency) we still use it after the unplug. My 
understanding (which could be wrong, since I haven't looked at this area 
a lot) is that the imported handle will stay valid even if the original 
device is closed and that's why I wanted to explicitly clean it up. 
It'll still be closed when the fd is closed so we're not doing something 
inherently bad, but I felt it would've been cleaner to have that in the 
common function. I'll add a note about it in the function description 
instead and keep the existing plug/unplug scheme.

Daniele

Patch
diff mbox

diff --git a/lib/igt_dummyload.c b/lib/igt_dummyload.c
index bb2be55..913cc93 100644
--- a/lib/igt_dummyload.c
+++ b/lib/igt_dummyload.c
@@ -29,6 +29,7 @@ 
 #include <i915_drm.h>
 
 #include "igt_core.h"
+#include "drmtest.h"
 #include "igt_dummyload.h"
 #include "igt_gt.h"
 #include "intel_chipset.h"
@@ -300,3 +301,77 @@  void igt_terminate_spin_batches(void)
 	igt_list_for_each(iter, &spin_list, link)
 		igt_spin_batch_end(iter);
 }
+
+/**
+ * igt_cork_new
+ * @fd: open drm file descriptor
+ *
+ * Imports a vgem bo with a fence attached to it. This bo can be used as a
+ * dependency during submission to stall execution until the fence is signaled.
+ *
+ * Returns:
+ * Structure with the handle of the imported bo and helper internal state
+ * for igt_cork_signal() and igt_cork_free().
+ */
+igt_cork_t *igt_cork_new(int fd)
+{
+	igt_cork_t *cork;
+	struct vgem_bo bo;
+	int dmabuf;
+
+	cork = calloc(1, sizeof(igt_cork_t));
+	igt_assert(cork);
+
+	cork->device = drm_open_driver(DRIVER_VGEM);
+
+	igt_require(vgem_has_fences(cork->device));
+
+	bo.width = bo.height = 1;
+	bo.bpp = 4;
+	vgem_create(cork->device, &bo);
+	cork->fence = vgem_fence_attach(cork->device, &bo, VGEM_FENCE_WRITE);
+
+	dmabuf = prime_handle_to_fd(cork->device, bo.handle);
+	cork->handle = prime_fd_to_handle(fd, dmabuf);
+	close(dmabuf);
+
+	return cork;
+}
+
+/**
+ * igt_cork_signal:
+ * @cork: cork state from igt_cork_new()
+ *
+ * This function signals the fence attached to the imported object, thus
+ * unblocking any stalled execution.
+ */
+void igt_cork_signal(igt_cork_t *cork)
+{
+	if (!cork)
+		return;
+
+	vgem_fence_signal(cork->device, cork->fence);
+	close(cork->device);
+	cork->device = 0;
+}
+
+/**
+ * igt_cork_free:
+ * @cork: cork state from igt_cork_new()
+ * @fd: open drm file descriptor
+ *
+ * This function signals the fence attached to the imported object (if it
+ * hasn't already been signaled by igt_cork_signal) and does the necessary
+ * post-processing.
+ */
+void igt_cork_free(int fd, igt_cork_t *cork)
+{
+	if (!cork)
+		return;
+
+	if (cork->device)
+		igt_cork_signal(cork);
+
+	gem_close(fd, cork->handle);
+	free(cork);
+}
diff --git a/lib/igt_dummyload.h b/lib/igt_dummyload.h
index 215425f..d20a867 100644
--- a/lib/igt_dummyload.h
+++ b/lib/igt_dummyload.h
@@ -29,6 +29,7 @@ 
 #include <time.h>
 
 #include "igt_aux.h"
+#include "igt_vgem.h"
 
 typedef struct igt_spin {
 	unsigned int handle;
@@ -51,4 +52,14 @@  void igt_spin_batch_free(int fd, igt_spin_t *spin);
 
 void igt_terminate_spin_batches(void);
 
+typedef struct igt_cork {
+	int device;
+	uint32_t handle;
+	uint32_t fence;
+} igt_cork_t;
+
+igt_cork_t *igt_cork_new(int fd);
+void igt_cork_signal(igt_cork_t *cork);
+void igt_cork_free(int fd, igt_cork_t *cork);
+
 #endif /* __IGT_DUMMYLOAD_H__ */