35,7 → 35,7 |
* of extra utility/tracking out of our acquire-ctx. This is provided |
* by drm_modeset_lock / drm_modeset_acquire_ctx. |
* |
* For basic principles of ww_mutex, see: Documentation/ww-mutex-design.txt |
* For basic principles of ww_mutex, see: Documentation/locking/ww-mutex-design.txt |
* |
* The basic usage pattern is to: |
* |
57,6 → 57,230 |
|
|
/** |
* __drm_modeset_lock_all - internal helper to grab all modeset locks |
* @dev: DRM device |
* @trylock: trylock mode for atomic contexts |
* |
* This is a special version of drm_modeset_lock_all() which can also be used in |
* atomic contexts. Then @trylock must be set to true. |
* |
* Returns: |
* 0 on success or negative error code on failure. |
*/ |
int __drm_modeset_lock_all(struct drm_device *dev, |
bool trylock) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx; |
int ret; |
|
ctx = kzalloc(sizeof(*ctx), |
trylock ? GFP_ATOMIC : GFP_KERNEL); |
if (!ctx) |
return -ENOMEM; |
|
if (trylock) { |
if (!mutex_trylock(&config->mutex)) |
return -EBUSY; |
} else { |
mutex_lock(&config->mutex); |
} |
|
drm_modeset_acquire_init(ctx, 0); |
ctx->trylock_only = trylock; |
|
retry: |
ret = drm_modeset_lock(&config->connection_mutex, ctx); |
if (ret) |
goto fail; |
ret = drm_modeset_lock_all_crtcs(dev, ctx); |
if (ret) |
goto fail; |
|
WARN_ON(config->acquire_ctx); |
|
/* now we hold the locks, so now that it is safe, stash the |
* ctx for drm_modeset_unlock_all(): |
*/ |
config->acquire_ctx = ctx; |
|
drm_warn_on_modeset_not_all_locked(dev); |
|
return 0; |
|
fail: |
if (ret == -EDEADLK) { |
drm_modeset_backoff(ctx); |
goto retry; |
} |
|
return ret; |
} |
EXPORT_SYMBOL(__drm_modeset_lock_all); |
|
/** |
* drm_modeset_lock_all - take all modeset locks |
* @dev: drm device |
* |
* This function takes all modeset locks, suitable where a more fine-grained |
* scheme isn't (yet) implemented. Locks must be dropped with |
* drm_modeset_unlock_all. |
*/ |
void drm_modeset_lock_all(struct drm_device *dev) |
{ |
WARN_ON(__drm_modeset_lock_all(dev, false) != 0); |
} |
EXPORT_SYMBOL(drm_modeset_lock_all); |
|
/** |
* drm_modeset_unlock_all - drop all modeset locks |
* @dev: device |
* |
* This function drop all modeset locks taken by drm_modeset_lock_all. |
*/ |
void drm_modeset_unlock_all(struct drm_device *dev) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; |
|
if (WARN_ON(!ctx)) |
return; |
|
config->acquire_ctx = NULL; |
drm_modeset_drop_locks(ctx); |
drm_modeset_acquire_fini(ctx); |
|
kfree(ctx); |
|
mutex_unlock(&dev->mode_config.mutex); |
} |
EXPORT_SYMBOL(drm_modeset_unlock_all); |
|
/** |
* drm_modeset_lock_crtc - lock crtc with hidden acquire ctx for a plane update |
* @crtc: DRM CRTC |
* @plane: DRM plane to be updated on @crtc |
* |
* This function locks the given crtc and plane (which should be either the |
* primary or cursor plane) using a hidden acquire context. This is necessary so |
* that drivers internally using the atomic interfaces can grab further locks |
* with the lock acquire context. |
* |
* Note that @plane can be NULL, e.g. when the cursor support hasn't yet been |
* converted to universal planes yet. |
*/ |
void drm_modeset_lock_crtc(struct drm_crtc *crtc, |
struct drm_plane *plane) |
{ |
struct drm_modeset_acquire_ctx *ctx; |
int ret; |
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
if (WARN_ON(!ctx)) |
return; |
|
drm_modeset_acquire_init(ctx, 0); |
|
retry: |
ret = drm_modeset_lock(&crtc->mutex, ctx); |
if (ret) |
goto fail; |
|
if (plane) { |
ret = drm_modeset_lock(&plane->mutex, ctx); |
if (ret) |
goto fail; |
|
if (plane->crtc) { |
ret = drm_modeset_lock(&plane->crtc->mutex, ctx); |
if (ret) |
goto fail; |
} |
} |
|
WARN_ON(crtc->acquire_ctx); |
|
/* now we hold the locks, so now that it is safe, stash the |
* ctx for drm_modeset_unlock_crtc(): |
*/ |
crtc->acquire_ctx = ctx; |
|
return; |
|
fail: |
if (ret == -EDEADLK) { |
drm_modeset_backoff(ctx); |
goto retry; |
} |
} |
EXPORT_SYMBOL(drm_modeset_lock_crtc); |
|
/** |
* drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls |
* @crtc: drm crtc |
* |
* Legacy ioctl operations like cursor updates or page flips only have per-crtc |
* locking, and store the acquire ctx in the corresponding crtc. All other |
* legacy operations take all locks and use a global acquire context. This |
* function grabs the right one. |
*/ |
struct drm_modeset_acquire_ctx * |
drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) |
{ |
if (crtc->acquire_ctx) |
return crtc->acquire_ctx; |
|
WARN_ON(!crtc->dev->mode_config.acquire_ctx); |
|
return crtc->dev->mode_config.acquire_ctx; |
} |
EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); |
|
/** |
* drm_modeset_unlock_crtc - drop crtc lock |
* @crtc: drm crtc |
* |
* This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other |
* locks acquired through the hidden context. |
*/ |
void drm_modeset_unlock_crtc(struct drm_crtc *crtc) |
{ |
struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx; |
|
if (WARN_ON(!ctx)) |
return; |
|
crtc->acquire_ctx = NULL; |
drm_modeset_drop_locks(ctx); |
drm_modeset_acquire_fini(ctx); |
|
kfree(ctx); |
} |
EXPORT_SYMBOL(drm_modeset_unlock_crtc); |
|
/** |
* drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked |
* @dev: device |
* |
* Useful as a debug assert. |
*/ |
void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) |
{ |
struct drm_crtc *crtc; |
|
/* Locking is currently fubar in the panic handler. */ |
// if (oops_in_progress) |
// return; |
|
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) |
WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); |
|
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); |
} |
EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); |
|
/** |
* drm_modeset_acquire_init - initialize acquire context |
* @ctx: the acquire context |
* @flags: for future |
108,7 → 332,12 |
|
WARN_ON(ctx->contended); |
|
if (interruptible && slow) { |
if (ctx->trylock_only) { |
if (!ww_mutex_trylock(&lock->mutex)) |
return -EBUSY; |
else |
return 0; |
} else if (interruptible && slow) { |
ret = ww_mutex_lock_slow_interruptible(&lock->mutex, &ctx->ww_ctx); |
} else if (interruptible) { |
ret = ww_mutex_lock_interruptible(&lock->mutex, &ctx->ww_ctx); |
226,15 → 455,14 |
} |
EXPORT_SYMBOL(drm_modeset_unlock); |
|
/* Temporary.. until we have sufficiently fine grained locking, there |
* are a couple scenarios where it is convenient to grab all crtc locks. |
* It is planned to remove this: |
*/ |
/* In some legacy codepaths it's convenient to just grab all the crtc and plane |
* related locks. */ |
int drm_modeset_lock_all_crtcs(struct drm_device *dev, |
struct drm_modeset_acquire_ctx *ctx) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_crtc *crtc; |
struct drm_plane *plane; |
int ret = 0; |
|
list_for_each_entry(crtc, &config->crtc_list, head) { |
243,6 → 471,12 |
return ret; |
} |
|
list_for_each_entry(plane, &config->plane_list, head) { |
ret = drm_modeset_lock(&plane->mutex, ctx); |
if (ret) |
return ret; |
} |
|
return 0; |
} |
EXPORT_SYMBOL(drm_modeset_lock_all_crtcs); |