Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f8bcc74 authored by Maarten Lankhorst's avatar Maarten Lankhorst
Browse files

drm/atomic: Prepare drm_modeset_lock infrastructure for interruptible waiting, v2.



When we want to make drm_atomic_commit interruptible, there are a lot of
places that call the lock function, which we don't have control over.

Rather than trying to convert every single one, it's easier to toggle
interruptible waiting per acquire_ctx. If drm_modeset_acquire_init is
called with DRM_MODESET_ACQUIRE_INTERRUPTIBLE, then we will perform
interruptible waits in drm_modeset_lock and drm_modeset_backoff.

Changes since v1:
- Fix locking example in drm_modeset_lock.c to be compatible
  with interruptible waiting (xexaxo) and make it default.
  Uninterruptible waiting shouldn't happen except in corner cases,
  but the example will still apply if the flag is removed.
- Add drm_modeset_lock_single_interruptible() to documentation.
- Fix dead link to removed drm_modeset_lock_interruptible() in
  drm_modeset_lock().

Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> #v1
Cc: Emil Velikov <emil.l.velikov@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20170912133749.6532-2-maarten.lankhorst@linux.intel.com


Reviewed-by: default avatarEmil Velikov <emil.l.velikov@gmail.com>
parent 9ab12e88
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -155,7 +155,7 @@ static int crtc_crc_open(struct inode *inode, struct file *filep)
	int ret = 0;
	int ret = 0;


	if (drm_drv_uses_atomic_modeset(crtc->dev)) {
	if (drm_drv_uses_atomic_modeset(crtc->dev)) {
		ret = drm_modeset_lock_interruptible(&crtc->mutex, NULL);
		ret = drm_modeset_lock_single_interruptible(&crtc->mutex);
		if (ret)
		if (ret)
			return ret;
			return ret;


+48 −48
Original line number Original line Diff line number Diff line
@@ -39,23 +39,28 @@
 *
 *
 * The basic usage pattern is to::
 * The basic usage pattern is to::
 *
 *
 *     drm_modeset_acquire_init(&ctx)
 *     drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
 *     retry:
 *     retry:
 *     foreach (lock in random_ordered_set_of_locks) {
 *     foreach (lock in random_ordered_set_of_locks) {
 *         ret = drm_modeset_lock(lock, &ctx)
 *         ret = drm_modeset_lock(lock, ctx)
 *         if (ret == -EDEADLK) {
 *         if (ret == -EDEADLK) {
 *             drm_modeset_backoff(&ctx);
 *             ret = drm_modeset_backoff(ctx);
 *             if (!ret)
 *                 goto retry;
 *                 goto retry;
 *         }
 *         }
 *         if (ret)
 *             goto out;
 *     }
 *     }
 *     ... do stuff ...
 *     ... do stuff ...
 *     drm_modeset_drop_locks(&ctx);
 *     out:
 *     drm_modeset_acquire_fini(&ctx);
 *     drm_modeset_drop_locks(ctx);
 *     drm_modeset_acquire_fini(ctx);
 *
 *
 * If all that is needed is a single modeset lock, then the &struct
 * If all that is needed is a single modeset lock, then the &struct
 * drm_modeset_acquire_ctx is not needed and the locking can be simplified
 * drm_modeset_acquire_ctx is not needed and the locking can be simplified
 * by passing a NULL instead of ctx in the drm_modeset_lock()
 * by passing a NULL instead of ctx in the drm_modeset_lock() call or
 * call and, when done, by calling drm_modeset_unlock().
 * calling  drm_modeset_lock_single_interruptible(). To unlock afterwards
 * call drm_modeset_unlock().
 *
 *
 * On top of these per-object locks using &ww_mutex there's also an overall
 * On top of these per-object locks using &ww_mutex there's also an overall
 * &drm_mode_config.mutex, for protecting everything else. Mostly this means
 * &drm_mode_config.mutex, for protecting everything else. Mostly this means
@@ -178,7 +183,11 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked);
/**
/**
 * drm_modeset_acquire_init - initialize acquire context
 * drm_modeset_acquire_init - initialize acquire context
 * @ctx: the acquire context
 * @ctx: the acquire context
 * @flags: for future
 * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE
 *
 * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags,
 * all calls to drm_modeset_lock() will perform an interruptible
 * wait.
 */
 */
void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
		uint32_t flags)
		uint32_t flags)
@@ -186,6 +195,9 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
	memset(ctx, 0, sizeof(*ctx));
	memset(ctx, 0, sizeof(*ctx));
	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
	ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class);
	INIT_LIST_HEAD(&ctx->locked);
	INIT_LIST_HEAD(&ctx->locked);

	if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE)
		ctx->interruptible = true;
}
}
EXPORT_SYMBOL(drm_modeset_acquire_init);
EXPORT_SYMBOL(drm_modeset_acquire_init);


@@ -261,8 +273,19 @@ static inline int modeset_lock(struct drm_modeset_lock *lock,
	return ret;
	return ret;
}
}


static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,
/**
		bool interruptible)
 * drm_modeset_backoff - deadlock avoidance backoff
 * @ctx: the acquire context
 *
 * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
 * you must call this function to drop all currently held locks and
 * block until the contended lock becomes available.
 *
 * This function returns 0 on success, or -ERESTARTSYS if this context
 * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the
 * wait has been interrupted.
 */
int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
{
{
	struct drm_modeset_lock *contended = ctx->contended;
	struct drm_modeset_lock *contended = ctx->contended;


@@ -273,35 +296,10 @@ static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx,


	drm_modeset_drop_locks(ctx);
	drm_modeset_drop_locks(ctx);


	return modeset_lock(contended, ctx, interruptible, true);
	return modeset_lock(contended, ctx, ctx->interruptible, true);
}

/**
 * drm_modeset_backoff - deadlock avoidance backoff
 * @ctx: the acquire context
 *
 * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK),
 * you must call this function to drop all currently held locks and
 * block until the contended lock becomes available.
 */
void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx)
{
	modeset_backoff(ctx, false);
}
}
EXPORT_SYMBOL(drm_modeset_backoff);
EXPORT_SYMBOL(drm_modeset_backoff);


/**
 * drm_modeset_backoff_interruptible - deadlock avoidance backoff
 * @ctx: the acquire context
 *
 * Interruptible version of drm_modeset_backoff()
 */
int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx)
{
	return modeset_backoff(ctx, true);
}
EXPORT_SYMBOL(drm_modeset_backoff_interruptible);

/**
/**
 * drm_modeset_lock_init - initialize lock
 * drm_modeset_lock_init - initialize lock
 * @lock: lock to init
 * @lock: lock to init
@@ -324,14 +322,18 @@ EXPORT_SYMBOL(drm_modeset_lock_init);
 * deadlock scenario has been detected and it is an error to attempt
 * deadlock scenario has been detected and it is an error to attempt
 * to take any more locks without first calling drm_modeset_backoff().
 * to take any more locks without first calling drm_modeset_backoff().
 *
 *
 * If the @ctx is not NULL and initialized with
 * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with
 * -ERESTARTSYS when interrupted.
 *
 * If @ctx is NULL then the function call behaves like a normal,
 * If @ctx is NULL then the function call behaves like a normal,
 * non-nesting mutex_lock() call.
 * uninterruptible non-nesting mutex_lock() call.
 */
 */
int drm_modeset_lock(struct drm_modeset_lock *lock,
int drm_modeset_lock(struct drm_modeset_lock *lock,
		struct drm_modeset_acquire_ctx *ctx)
		struct drm_modeset_acquire_ctx *ctx)
{
{
	if (ctx)
	if (ctx)
		return modeset_lock(lock, ctx, false, false);
		return modeset_lock(lock, ctx, ctx->interruptible, false);


	ww_mutex_lock(&lock->mutex, NULL);
	ww_mutex_lock(&lock->mutex, NULL);
	return 0;
	return 0;
@@ -339,21 +341,19 @@ int drm_modeset_lock(struct drm_modeset_lock *lock,
EXPORT_SYMBOL(drm_modeset_lock);
EXPORT_SYMBOL(drm_modeset_lock);


/**
/**
 * drm_modeset_lock_interruptible - take modeset lock
 * drm_modeset_lock_single_interruptible - take a single modeset lock
 * @lock: lock to take
 * @lock: lock to take
 * @ctx: acquire ctx
 *
 *
 * Interruptible version of drm_modeset_lock()
 * This function behaves as drm_modeset_lock() with a NULL context,
 * but performs interruptible waits.
 *
 * This function returns 0 on success, or -ERESTARTSYS when interrupted.
 */
 */
int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock)
		struct drm_modeset_acquire_ctx *ctx)
{
{
	if (ctx)
		return modeset_lock(lock, ctx, true, false);

	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
	return ww_mutex_lock_interruptible(&lock->mutex, NULL);
}
}
EXPORT_SYMBOL(drm_modeset_lock_interruptible);
EXPORT_SYMBOL(drm_modeset_lock_single_interruptible);


/**
/**
 * drm_modeset_unlock - drop modeset lock
 * drm_modeset_unlock - drop modeset lock
+8 −4
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ struct drm_modeset_lock;
 * @contended: used internally for -EDEADLK handling
 * @contended: used internally for -EDEADLK handling
 * @locked: list of held locks
 * @locked: list of held locks
 * @trylock_only: trylock mode used in atomic contexts/panic notifiers
 * @trylock_only: trylock mode used in atomic contexts/panic notifiers
 * @interruptible: whether interruptible locking should be used.
 *
 *
 * Each thread competing for a set of locks must use one acquire
 * Each thread competing for a set of locks must use one acquire
 * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
 * ctx.  And if any lock fxn returns -EDEADLK, it must backoff and
@@ -59,6 +60,9 @@ struct drm_modeset_acquire_ctx {
	 * Trylock mode, use only for panic handlers!
	 * Trylock mode, use only for panic handlers!
	 */
	 */
	bool trylock_only;
	bool trylock_only;

	/* Perform interruptible waits on this context. */
	bool interruptible;
};
};


/**
/**
@@ -82,12 +86,13 @@ struct drm_modeset_lock {
	struct list_head head;
	struct list_head head;
};
};


#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0)

void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx,
		uint32_t flags);
		uint32_t flags);
void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx);
void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx);
void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx);
int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx);


void drm_modeset_lock_init(struct drm_modeset_lock *lock);
void drm_modeset_lock_init(struct drm_modeset_lock *lock);


@@ -111,8 +116,7 @@ static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock)


int drm_modeset_lock(struct drm_modeset_lock *lock,
int drm_modeset_lock(struct drm_modeset_lock *lock,
		struct drm_modeset_acquire_ctx *ctx);
		struct drm_modeset_acquire_ctx *ctx);
int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock,
int __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock);
		struct drm_modeset_acquire_ctx *ctx);
void drm_modeset_unlock(struct drm_modeset_lock *lock);
void drm_modeset_unlock(struct drm_modeset_lock *lock);


struct drm_device;
struct drm_device;