drm/nouveau: add per-channel mutex, use to lock access to drm's channel
This fixes a race condition between fbcon acceleration and TTM buffer
moves. To reproduce:
- start X
- switch to vt and "while (true); do dmesg; done"
- switch to another vt and "sleep 2 && cat /path/to/debugfs/dri/0/evict_vram"
- switch back to vt running dmesg
We don't make use of this on any other channel yet, they're currently
protected by drm_global_mutex. This will change in the near future.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 22e83ad..bc30dbe 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -62,11 +62,13 @@
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ mutex_lock(&dev_priv->channel->mutex);
if (dev_priv->card_type < NV_50)
ret = nv04_fbcon_fillrect(info, rect);
else
if (dev_priv->card_type < NV_C0)
ret = nv50_fbcon_fillrect(info, rect);
+ mutex_unlock(&dev_priv->channel->mutex);
}
if (ret == 0)
@@ -90,11 +92,13 @@
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ mutex_lock(&dev_priv->channel->mutex);
if (dev_priv->card_type < NV_50)
ret = nv04_fbcon_copyarea(info, image);
else
if (dev_priv->card_type < NV_C0)
ret = nv50_fbcon_copyarea(info, image);
+ mutex_unlock(&dev_priv->channel->mutex);
}
if (ret == 0)
@@ -118,11 +122,13 @@
ret = -ENODEV;
if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED)) {
+ mutex_lock(&dev_priv->channel->mutex);
if (dev_priv->card_type < NV_50)
ret = nv04_fbcon_imageblit(info, image);
else
if (dev_priv->card_type < NV_C0)
ret = nv50_fbcon_imageblit(info, image);
+ mutex_unlock(&dev_priv->channel->mutex);
}
if (ret == 0)
@@ -142,12 +148,15 @@
struct nouveau_channel *chan = dev_priv->channel;
int ret, i;
- if (!chan || !chan->accel_done ||
+ if (!chan || !chan->accel_done || in_interrupt() ||
info->state != FBINFO_STATE_RUNNING ||
info->flags & FBINFO_HWACCEL_DISABLED)
return 0;
- if (RING_SPACE(chan, 4)) {
+ mutex_lock(&chan->mutex);
+ ret = RING_SPACE(chan, 4);
+ if (ret) {
+ mutex_unlock(&chan->mutex);
nouveau_fbcon_gpu_lockup(info);
return 0;
}
@@ -158,6 +167,7 @@
OUT_RING(chan, 0);
nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
FIRE_RING(chan);
+ mutex_unlock(&chan->mutex);
ret = -EBUSY;
for (i = 0; i < 100000; i++) {
@@ -353,6 +363,8 @@
info->pixmap.flags = FB_PIXMAP_SYSTEM;
info->pixmap.scan_align = 1;
+ mutex_unlock(&dev->struct_mutex);
+
if (dev_priv->channel && !nouveau_nofbaccel) {
ret = -ENODEV;
if (dev_priv->card_type < NV_50)
@@ -373,7 +385,6 @@
nouveau_fb->base.height,
nvbo->bo.offset, nvbo);
- mutex_unlock(&dev->struct_mutex);
vga_switcheroo_client_fb_set(dev->pdev, info);
return 0;