diff mbox series

[08/29] drm/i915: Introduce struct intel_wakeref

Message ID 20190408091728.20207-8-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show
Series [01/29] drm/i915: Mark up ips for RCU protection | expand

Commit Message

Chris Wilson April 8, 2019, 9:17 a.m. UTC
For controlling runtime pm of the GT and engines, we would like to have
a callback to do extra work the first time we wake up and the last time
we drop the wakeref. This first/last access needs serialisation and so
we encompass a mutex with the regular intel_wakeref_t tracker.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/Makefile             |  1 +
 drivers/gpu/drm/i915/Makefile.header-test |  3 +-
 drivers/gpu/drm/i915/i915_drv.h           |  3 +-
 drivers/gpu/drm/i915/intel_wakeref.c      | 51 +++++++++++++++
 drivers/gpu/drm/i915/intel_wakeref.h      | 77 +++++++++++++++++++++++
 5 files changed, 132 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_wakeref.c
 create mode 100644 drivers/gpu/drm/i915/intel_wakeref.h

Comments

Tvrtko Ursulin April 10, 2019, 9:49 a.m. UTC | #1
On 08/04/2019 10:17, Chris Wilson wrote:
> For controlling runtime pm of the GT and engines, we would like to have
> a callback to do extra work the first time we wake up and the last time
> we drop the wakeref. This first/last access needs serialisation and so
> we encompass a mutex with the regular intel_wakeref_t tracker.
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> ---
>   drivers/gpu/drm/i915/Makefile             |  1 +
>   drivers/gpu/drm/i915/Makefile.header-test |  3 +-
>   drivers/gpu/drm/i915/i915_drv.h           |  3 +-
>   drivers/gpu/drm/i915/intel_wakeref.c      | 51 +++++++++++++++
>   drivers/gpu/drm/i915/intel_wakeref.h      | 77 +++++++++++++++++++++++
>   5 files changed, 132 insertions(+), 3 deletions(-)
>   create mode 100644 drivers/gpu/drm/i915/intel_wakeref.c
>   create mode 100644 drivers/gpu/drm/i915/intel_wakeref.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 40130cf5c003..233bad5e361f 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -50,6 +50,7 @@ i915-y += i915_drv.o \
>   	  intel_device_info.o \
>   	  intel_pm.o \
>   	  intel_runtime_pm.o \
> +	  intel_wakeref.o \
>   	  intel_uncore.o
>   
>   # core library code
> diff --git a/drivers/gpu/drm/i915/Makefile.header-test b/drivers/gpu/drm/i915/Makefile.header-test
> index 96a5d90629ec..e6b3e7588860 100644
> --- a/drivers/gpu/drm/i915/Makefile.header-test
> +++ b/drivers/gpu/drm/i915/Makefile.header-test
> @@ -31,7 +31,8 @@ header_test := \
>   	intel_psr.h \
>   	intel_sdvo.h \
>   	intel_sprite.h \
> -	intel_tv.h
> +	intel_tv.h \
> +	intel_wakeref.h
>   
>   quiet_cmd_header_test = HDRTEST $@
>         cmd_header_test = echo "\#include \"$(<F)\"" > $@
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index fad5306f07da..62a7e91acd7f 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -74,6 +74,7 @@
>   #include "intel_opregion.h"
>   #include "intel_uc.h"
>   #include "intel_uncore.h"
> +#include "intel_wakeref.h"
>   #include "intel_wopcm.h"
>   
>   #include "i915_gem.h"
> @@ -134,8 +135,6 @@ bool i915_error_injected(void);
>   	__i915_printk(i915, i915_error_injected() ? KERN_DEBUG : KERN_ERR, \
>   		      fmt, ##__VA_ARGS__)
>   
> -typedef depot_stack_handle_t intel_wakeref_t;
> -
>   enum hpd_pin {
>   	HPD_NONE = 0,
>   	HPD_TV = HPD_NONE,     /* TV is known to be unreliable */
> diff --git a/drivers/gpu/drm/i915/intel_wakeref.c b/drivers/gpu/drm/i915/intel_wakeref.c
> new file mode 100644
> index 000000000000..c133c91cf277
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_wakeref.c
> @@ -0,0 +1,51 @@
> +/*
> + * SPDX-License-Identifier: MIT
> + *
> + * Copyright © 2019 Intel Corporation
> + */
> +
> +#include "intel_drv.h"
> +#include "intel_wakeref.h"
> +
> +void __intel_wakeref_get_once(struct drm_i915_private *i915,
> +			      struct intel_wakeref_count *wc,
> +			      bool (*fn)(struct intel_wakeref_count *wc))
> +{
> +	/*
> +	 * Treat get/put as different subclasses, as we may need to run
> +	 * the put callback from under the shrinker and do not want to
> +	 * cross-contanimate that callback with any extra work performed
> +	 * upon acquiring the wakeref.
> +	 */
> +	mutex_lock_nested(&wc->mutex, SINGLE_DEPTH_NESTING);
> +	if (!atomic_read(&wc->count)) {
> +		wc->wakeref = intel_runtime_pm_get(i915);
> +		if (unlikely(!fn(wc))) {
> +			intel_runtime_pm_put(i915, wc->wakeref);
> +			mutex_unlock(&wc->mutex);
> +			return;

Feels like a bad API that callback return error gets ignored. I mean 
from the caller's perspective - how do they know if the wakeref get 
succeeded or not?

> +		}
> +
> +		smp_mb__before_atomic(); /* release wc->count */
> +	}
> +	atomic_inc(&wc->count);
> +	mutex_unlock(&wc->mutex);
> +}
> +
> +void __intel_wakeref_put_once(struct drm_i915_private *i915,
> +			      struct intel_wakeref_count *wc,
> +			      bool (*fn)(struct intel_wakeref_count *wc))
> +{
> +	if (likely(fn(wc)))
> +		intel_runtime_pm_put(i915, wc->wakeref);
> +	else
> +		atomic_inc(&wc->count);
> +	mutex_unlock(&wc->mutex);
> +}
> +
> +void __intel_wakeref_init(struct intel_wakeref_count *wc,
> +			  struct lock_class_key *key)
> +{
> +	__mutex_init(&wc->mutex, "wakeref", key);
> +	atomic_set(&wc->count, 0);
> +}
> diff --git a/drivers/gpu/drm/i915/intel_wakeref.h b/drivers/gpu/drm/i915/intel_wakeref.h
> new file mode 100644
> index 000000000000..cd4d8f57e502
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/intel_wakeref.h
> @@ -0,0 +1,77 @@
> +/*
> + * SPDX-License-Identifier: MIT
> + *
> + * Copyright © 2019 Intel Corporation
> + */
> +
> +#ifndef INTEL_WAKEREF_H
> +#define INTEL_WAKEREF_H
> +
> +#include <linux/atomic.h>
> +#include <linux/mutex.h>
> +#include <linux/stackdepot.h>
> +
> +struct drm_i915_private;
> +
> +typedef depot_stack_handle_t intel_wakeref_t;
> +
> +struct intel_wakeref_count {
> +	atomic_t count;
> +	struct mutex mutex;
> +	intel_wakeref_t wakeref;
> +};
> +
> +void __intel_wakeref_init(struct intel_wakeref_count *wc,
> +			  struct lock_class_key *key);
> +#define intel_wakeref_init(wc) do {					\
> +	static struct lock_class_key __key;				\
> +									\
> +	__intel_wakeref_init((wc), &__key);				\
> +} while (0)
> +
> +void __intel_wakeref_get_once(struct drm_i915_private *i915,
> +			      struct intel_wakeref_count *wc,
> +			      bool (*fn)(struct intel_wakeref_count *wc));
> +void __intel_wakeref_put_once(struct drm_i915_private *i915,
> +			      struct intel_wakeref_count *wc,
> +			      bool (*fn)(struct intel_wakeref_count *wc));
> +
> +static inline void
> +intel_wakeref_get_once(struct drm_i915_private *i915,
> +		       struct intel_wakeref_count *wc,
> +		       bool (*fn)(struct intel_wakeref_count *wc))

What does the "once" suffix refer to here? That it will only call the 
callback once? Is that needed in the function name and couldn't be 
documented in kerneldoc, given how there is no non-suffixed version?

> +{
> +	if (unlikely(!atomic_inc_not_zero(&wc->count)))
> +		__intel_wakeref_get_once(i915, wc, fn);
> +}
> +
> +static inline void
> +intel_wakeref_put_once(struct drm_i915_private *i915,
> +		       struct intel_wakeref_count *wc,
> +		       bool (*fn)(struct intel_wakeref_count *wc))
> +{
> +	if (atomic_dec_and_mutex_lock(&wc->count, &wc->mutex))
> +		__intel_wakeref_put_once(i915, wc, fn);
> +}
> +
> +static inline void
> +intel_wakeref_lock(struct intel_wakeref_count *wc)
> +	__acquires(wc->mutex)
> +{
> +	mutex_lock(&wc->mutex);
> +}
> +
> +static inline void
> +intel_wakeref_unlock(struct intel_wakeref_count *wc)
> +	__releases(wc->mutex)
> +{
> +	mutex_unlock(&wc->mutex);
> +}
> +
> +static inline bool
> +intel_wakeref_active(struct intel_wakeref_count *wc)
> +{
> +	return atomic_read(&wc->count);
> +}
> +
> +#endif /* INTEL_WAKEREF_H */
> 

Regards,

Tvrtko
Chris Wilson April 10, 2019, 10:01 a.m. UTC | #2
Quoting Tvrtko Ursulin (2019-04-10 10:49:35)
> 
> On 08/04/2019 10:17, Chris Wilson wrote:
> > For controlling runtime pm of the GT and engines, we would like to have
> > a callback to do extra work the first time we wake up and the last time
> > we drop the wakeref. This first/last access needs serialisation and so
> > we encompass a mutex with the regular intel_wakeref_t tracker.
> > 
> > Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> > ---
> >   drivers/gpu/drm/i915/Makefile             |  1 +
> >   drivers/gpu/drm/i915/Makefile.header-test |  3 +-
> >   drivers/gpu/drm/i915/i915_drv.h           |  3 +-
> >   drivers/gpu/drm/i915/intel_wakeref.c      | 51 +++++++++++++++
> >   drivers/gpu/drm/i915/intel_wakeref.h      | 77 +++++++++++++++++++++++
> >   5 files changed, 132 insertions(+), 3 deletions(-)
> >   create mode 100644 drivers/gpu/drm/i915/intel_wakeref.c
> >   create mode 100644 drivers/gpu/drm/i915/intel_wakeref.h
> > 
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index 40130cf5c003..233bad5e361f 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -50,6 +50,7 @@ i915-y += i915_drv.o \
> >         intel_device_info.o \
> >         intel_pm.o \
> >         intel_runtime_pm.o \
> > +       intel_wakeref.o \
> >         intel_uncore.o
> >   
> >   # core library code
> > diff --git a/drivers/gpu/drm/i915/Makefile.header-test b/drivers/gpu/drm/i915/Makefile.header-test
> > index 96a5d90629ec..e6b3e7588860 100644
> > --- a/drivers/gpu/drm/i915/Makefile.header-test
> > +++ b/drivers/gpu/drm/i915/Makefile.header-test
> > @@ -31,7 +31,8 @@ header_test := \
> >       intel_psr.h \
> >       intel_sdvo.h \
> >       intel_sprite.h \
> > -     intel_tv.h
> > +     intel_tv.h \
> > +     intel_wakeref.h
> >   
> >   quiet_cmd_header_test = HDRTEST $@
> >         cmd_header_test = echo "\#include \"$(<F)\"" > $@
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index fad5306f07da..62a7e91acd7f 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -74,6 +74,7 @@
> >   #include "intel_opregion.h"
> >   #include "intel_uc.h"
> >   #include "intel_uncore.h"
> > +#include "intel_wakeref.h"
> >   #include "intel_wopcm.h"
> >   
> >   #include "i915_gem.h"
> > @@ -134,8 +135,6 @@ bool i915_error_injected(void);
> >       __i915_printk(i915, i915_error_injected() ? KERN_DEBUG : KERN_ERR, \
> >                     fmt, ##__VA_ARGS__)
> >   
> > -typedef depot_stack_handle_t intel_wakeref_t;
> > -
> >   enum hpd_pin {
> >       HPD_NONE = 0,
> >       HPD_TV = HPD_NONE,     /* TV is known to be unreliable */
> > diff --git a/drivers/gpu/drm/i915/intel_wakeref.c b/drivers/gpu/drm/i915/intel_wakeref.c
> > new file mode 100644
> > index 000000000000..c133c91cf277
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/intel_wakeref.c
> > @@ -0,0 +1,51 @@
> > +/*
> > + * SPDX-License-Identifier: MIT
> > + *
> > + * Copyright © 2019 Intel Corporation
> > + */
> > +
> > +#include "intel_drv.h"
> > +#include "intel_wakeref.h"
> > +
> > +void __intel_wakeref_get_once(struct drm_i915_private *i915,
> > +                           struct intel_wakeref_count *wc,
> > +                           bool (*fn)(struct intel_wakeref_count *wc))
> > +{
> > +     /*
> > +      * Treat get/put as different subclasses, as we may need to run
> > +      * the put callback from under the shrinker and do not want to
> > +      * cross-contanimate that callback with any extra work performed
> > +      * upon acquiring the wakeref.
> > +      */
> > +     mutex_lock_nested(&wc->mutex, SINGLE_DEPTH_NESTING);
> > +     if (!atomic_read(&wc->count)) {
> > +             wc->wakeref = intel_runtime_pm_get(i915);
> > +             if (unlikely(!fn(wc))) {
> > +                     intel_runtime_pm_put(i915, wc->wakeref);
> > +                     mutex_unlock(&wc->mutex);
> > +                     return;
> 
> Feels like a bad API that callback return error gets ignored. I mean 
> from the caller's perspective - how do they know if the wakeref get 
> succeeded or not?

Didn't think get failing through, as I just added it to try and make it
look similar to the put case (which is one being used to postpone the
wakeref release).

Propagate seems reasonable. For put, we'll just ignore as we try again
later, eventually wedging the device as a get-out-of-jail. For get
failing, we should abort the operation in the caller, and return an error
to the user.

> > +static inline void
> > +intel_wakeref_get_once(struct drm_i915_private *i915,
> > +                    struct intel_wakeref_count *wc,
> > +                    bool (*fn)(struct intel_wakeref_count *wc))
> 
> What does the "once" suffix refer to here? That it will only call the 
> callback once? Is that needed in the function name and couldn't be 
> documented in kerneldoc, given how there is no non-suffixed version?

I was thinking of 'works like pthread_once()' in that we only
acquire/release the wakeref once. Yeah, that may be overthinking it and
makes it look clumsy. We certainly don't do 'pin_once', and just accept
that the first/last pinning are the heavy ones. The only difference here
is that we are wrapping a callback.
-Chris
Tvrtko Ursulin April 10, 2019, 10:07 a.m. UTC | #3
On 10/04/2019 11:01, Chris Wilson wrote:
> Quoting Tvrtko Ursulin (2019-04-10 10:49:35)
>>
>> On 08/04/2019 10:17, Chris Wilson wrote:
>>> For controlling runtime pm of the GT and engines, we would like to have
>>> a callback to do extra work the first time we wake up and the last time
>>> we drop the wakeref. This first/last access needs serialisation and so
>>> we encompass a mutex with the regular intel_wakeref_t tracker.
>>>
>>> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
>>> ---
>>>    drivers/gpu/drm/i915/Makefile             |  1 +
>>>    drivers/gpu/drm/i915/Makefile.header-test |  3 +-
>>>    drivers/gpu/drm/i915/i915_drv.h           |  3 +-
>>>    drivers/gpu/drm/i915/intel_wakeref.c      | 51 +++++++++++++++
>>>    drivers/gpu/drm/i915/intel_wakeref.h      | 77 +++++++++++++++++++++++
>>>    5 files changed, 132 insertions(+), 3 deletions(-)
>>>    create mode 100644 drivers/gpu/drm/i915/intel_wakeref.c
>>>    create mode 100644 drivers/gpu/drm/i915/intel_wakeref.h
>>>
>>> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>>> index 40130cf5c003..233bad5e361f 100644
>>> --- a/drivers/gpu/drm/i915/Makefile
>>> +++ b/drivers/gpu/drm/i915/Makefile
>>> @@ -50,6 +50,7 @@ i915-y += i915_drv.o \
>>>          intel_device_info.o \
>>>          intel_pm.o \
>>>          intel_runtime_pm.o \
>>> +       intel_wakeref.o \
>>>          intel_uncore.o
>>>    
>>>    # core library code
>>> diff --git a/drivers/gpu/drm/i915/Makefile.header-test b/drivers/gpu/drm/i915/Makefile.header-test
>>> index 96a5d90629ec..e6b3e7588860 100644
>>> --- a/drivers/gpu/drm/i915/Makefile.header-test
>>> +++ b/drivers/gpu/drm/i915/Makefile.header-test
>>> @@ -31,7 +31,8 @@ header_test := \
>>>        intel_psr.h \
>>>        intel_sdvo.h \
>>>        intel_sprite.h \
>>> -     intel_tv.h
>>> +     intel_tv.h \
>>> +     intel_wakeref.h
>>>    
>>>    quiet_cmd_header_test = HDRTEST $@
>>>          cmd_header_test = echo "\#include \"$(<F)\"" > $@
>>> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
>>> index fad5306f07da..62a7e91acd7f 100644
>>> --- a/drivers/gpu/drm/i915/i915_drv.h
>>> +++ b/drivers/gpu/drm/i915/i915_drv.h
>>> @@ -74,6 +74,7 @@
>>>    #include "intel_opregion.h"
>>>    #include "intel_uc.h"
>>>    #include "intel_uncore.h"
>>> +#include "intel_wakeref.h"
>>>    #include "intel_wopcm.h"
>>>    
>>>    #include "i915_gem.h"
>>> @@ -134,8 +135,6 @@ bool i915_error_injected(void);
>>>        __i915_printk(i915, i915_error_injected() ? KERN_DEBUG : KERN_ERR, \
>>>                      fmt, ##__VA_ARGS__)
>>>    
>>> -typedef depot_stack_handle_t intel_wakeref_t;
>>> -
>>>    enum hpd_pin {
>>>        HPD_NONE = 0,
>>>        HPD_TV = HPD_NONE,     /* TV is known to be unreliable */
>>> diff --git a/drivers/gpu/drm/i915/intel_wakeref.c b/drivers/gpu/drm/i915/intel_wakeref.c
>>> new file mode 100644
>>> index 000000000000..c133c91cf277
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/i915/intel_wakeref.c
>>> @@ -0,0 +1,51 @@
>>> +/*
>>> + * SPDX-License-Identifier: MIT
>>> + *
>>> + * Copyright © 2019 Intel Corporation
>>> + */
>>> +
>>> +#include "intel_drv.h"
>>> +#include "intel_wakeref.h"
>>> +
>>> +void __intel_wakeref_get_once(struct drm_i915_private *i915,
>>> +                           struct intel_wakeref_count *wc,
>>> +                           bool (*fn)(struct intel_wakeref_count *wc))
>>> +{
>>> +     /*
>>> +      * Treat get/put as different subclasses, as we may need to run
>>> +      * the put callback from under the shrinker and do not want to
>>> +      * cross-contanimate that callback with any extra work performed
>>> +      * upon acquiring the wakeref.
>>> +      */
>>> +     mutex_lock_nested(&wc->mutex, SINGLE_DEPTH_NESTING);
>>> +     if (!atomic_read(&wc->count)) {
>>> +             wc->wakeref = intel_runtime_pm_get(i915);
>>> +             if (unlikely(!fn(wc))) {
>>> +                     intel_runtime_pm_put(i915, wc->wakeref);
>>> +                     mutex_unlock(&wc->mutex);
>>> +                     return;
>>
>> Feels like a bad API that callback return error gets ignored. I mean
>> from the caller's perspective - how do they know if the wakeref get
>> succeeded or not?
> 
> Didn't think get failing through, as I just added it to try and make it
> look similar to the put case (which is one being used to postpone the
> wakeref release).
> 
> Propagate seems reasonable. For put, we'll just ignore as we try again
> later, eventually wedging the device as a get-out-of-jail. For get
> failing, we should abort the operation in the caller, and return an error
> to the user.

If you need the ability to fail sure. Just I did not see it in one later 
patch which uses it so if there aren't any other then maybe even make 
the callback have no return value and simplify the callers. Any of the 
two works for me.

Regards,

Tvrtko

>>> +static inline void
>>> +intel_wakeref_get_once(struct drm_i915_private *i915,
>>> +                    struct intel_wakeref_count *wc,
>>> +                    bool (*fn)(struct intel_wakeref_count *wc))
>>
>> What does the "once" suffix refer to here? That it will only call the
>> callback once? Is that needed in the function name and couldn't be
>> documented in kerneldoc, given how there is no non-suffixed version?
> 
> I was thinking of 'works like pthread_once()' in that we only
> acquire/release the wakeref once. Yeah, that may be overthinking it and
> makes it look clumsy. We certainly don't do 'pin_once', and just accept
> that the first/last pinning are the heavy ones. The only difference here
> is that we are wrapping a callback.
> -Chris
>
diff mbox series

Patch

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 40130cf5c003..233bad5e361f 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -50,6 +50,7 @@  i915-y += i915_drv.o \
 	  intel_device_info.o \
 	  intel_pm.o \
 	  intel_runtime_pm.o \
+	  intel_wakeref.o \
 	  intel_uncore.o
 
 # core library code
diff --git a/drivers/gpu/drm/i915/Makefile.header-test b/drivers/gpu/drm/i915/Makefile.header-test
index 96a5d90629ec..e6b3e7588860 100644
--- a/drivers/gpu/drm/i915/Makefile.header-test
+++ b/drivers/gpu/drm/i915/Makefile.header-test
@@ -31,7 +31,8 @@  header_test := \
 	intel_psr.h \
 	intel_sdvo.h \
 	intel_sprite.h \
-	intel_tv.h
+	intel_tv.h \
+	intel_wakeref.h
 
 quiet_cmd_header_test = HDRTEST $@
       cmd_header_test = echo "\#include \"$(<F)\"" > $@
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index fad5306f07da..62a7e91acd7f 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -74,6 +74,7 @@ 
 #include "intel_opregion.h"
 #include "intel_uc.h"
 #include "intel_uncore.h"
+#include "intel_wakeref.h"
 #include "intel_wopcm.h"
 
 #include "i915_gem.h"
@@ -134,8 +135,6 @@  bool i915_error_injected(void);
 	__i915_printk(i915, i915_error_injected() ? KERN_DEBUG : KERN_ERR, \
 		      fmt, ##__VA_ARGS__)
 
-typedef depot_stack_handle_t intel_wakeref_t;
-
 enum hpd_pin {
 	HPD_NONE = 0,
 	HPD_TV = HPD_NONE,     /* TV is known to be unreliable */
diff --git a/drivers/gpu/drm/i915/intel_wakeref.c b/drivers/gpu/drm/i915/intel_wakeref.c
new file mode 100644
index 000000000000..c133c91cf277
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_wakeref.c
@@ -0,0 +1,51 @@ 
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#include "intel_drv.h"
+#include "intel_wakeref.h"
+
+void __intel_wakeref_get_once(struct drm_i915_private *i915,
+			      struct intel_wakeref_count *wc,
+			      bool (*fn)(struct intel_wakeref_count *wc))
+{
+	/*
+	 * Treat get/put as different subclasses, as we may need to run
+	 * the put callback from under the shrinker and do not want to
+	 * cross-contanimate that callback with any extra work performed
+	 * upon acquiring the wakeref.
+	 */
+	mutex_lock_nested(&wc->mutex, SINGLE_DEPTH_NESTING);
+	if (!atomic_read(&wc->count)) {
+		wc->wakeref = intel_runtime_pm_get(i915);
+		if (unlikely(!fn(wc))) {
+			intel_runtime_pm_put(i915, wc->wakeref);
+			mutex_unlock(&wc->mutex);
+			return;
+		}
+
+		smp_mb__before_atomic(); /* release wc->count */
+	}
+	atomic_inc(&wc->count);
+	mutex_unlock(&wc->mutex);
+}
+
+void __intel_wakeref_put_once(struct drm_i915_private *i915,
+			      struct intel_wakeref_count *wc,
+			      bool (*fn)(struct intel_wakeref_count *wc))
+{
+	if (likely(fn(wc)))
+		intel_runtime_pm_put(i915, wc->wakeref);
+	else
+		atomic_inc(&wc->count);
+	mutex_unlock(&wc->mutex);
+}
+
+void __intel_wakeref_init(struct intel_wakeref_count *wc,
+			  struct lock_class_key *key)
+{
+	__mutex_init(&wc->mutex, "wakeref", key);
+	atomic_set(&wc->count, 0);
+}
diff --git a/drivers/gpu/drm/i915/intel_wakeref.h b/drivers/gpu/drm/i915/intel_wakeref.h
new file mode 100644
index 000000000000..cd4d8f57e502
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_wakeref.h
@@ -0,0 +1,77 @@ 
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2019 Intel Corporation
+ */
+
+#ifndef INTEL_WAKEREF_H
+#define INTEL_WAKEREF_H
+
+#include <linux/atomic.h>
+#include <linux/mutex.h>
+#include <linux/stackdepot.h>
+
+struct drm_i915_private;
+
+typedef depot_stack_handle_t intel_wakeref_t;
+
+struct intel_wakeref_count {
+	atomic_t count;
+	struct mutex mutex;
+	intel_wakeref_t wakeref;
+};
+
+void __intel_wakeref_init(struct intel_wakeref_count *wc,
+			  struct lock_class_key *key);
+#define intel_wakeref_init(wc) do {					\
+	static struct lock_class_key __key;				\
+									\
+	__intel_wakeref_init((wc), &__key);				\
+} while (0)
+
+void __intel_wakeref_get_once(struct drm_i915_private *i915,
+			      struct intel_wakeref_count *wc,
+			      bool (*fn)(struct intel_wakeref_count *wc));
+void __intel_wakeref_put_once(struct drm_i915_private *i915,
+			      struct intel_wakeref_count *wc,
+			      bool (*fn)(struct intel_wakeref_count *wc));
+
+static inline void
+intel_wakeref_get_once(struct drm_i915_private *i915,
+		       struct intel_wakeref_count *wc,
+		       bool (*fn)(struct intel_wakeref_count *wc))
+{
+	if (unlikely(!atomic_inc_not_zero(&wc->count)))
+		__intel_wakeref_get_once(i915, wc, fn);
+}
+
+static inline void
+intel_wakeref_put_once(struct drm_i915_private *i915,
+		       struct intel_wakeref_count *wc,
+		       bool (*fn)(struct intel_wakeref_count *wc))
+{
+	if (atomic_dec_and_mutex_lock(&wc->count, &wc->mutex))
+		__intel_wakeref_put_once(i915, wc, fn);
+}
+
+static inline void
+intel_wakeref_lock(struct intel_wakeref_count *wc)
+	__acquires(wc->mutex)
+{
+	mutex_lock(&wc->mutex);
+}
+
+static inline void
+intel_wakeref_unlock(struct intel_wakeref_count *wc)
+	__releases(wc->mutex)
+{
+	mutex_unlock(&wc->mutex);
+}
+
+static inline bool
+intel_wakeref_active(struct intel_wakeref_count *wc)
+{
+	return atomic_read(&wc->count);
+}
+
+#endif /* INTEL_WAKEREF_H */