Egbert and I have been looking into the issues that are preventing a second
X server to be started for i810/830M platforms when DRI is enabled. Several
issues were found:
1. The i810 support doesn't unbind/release the agpgart module when VT
switching away, and this prevents a second X server from acquiring
it. Since the i810 driver requires agpgart access even for 2D
operation, this is prevents a second X server from running. A fix
for this is to add the unbind/release calls at LeaveVT, and
acquire/rebind at EnterVT. The attached patch does this. It also
makes a small change to the unbind code in the DRM kernel modules
to handle this.
2. The 830M support does unbind/release the agpgart module, but code
in DRM(release) called when closing a /dev/dri/* device blocks
until it can obtain the lock. Since the first server holds the
lock while switched away, the second one can never get it, so it
ends up blocking in close(). The second server opens/closes the
devices to find out whether DRI can be enabled. DRI can't be
enabled on the second server (which isn't a problem), but this
blocking behaviour is preventing it from starting up at all. I've
found that this can be solved by keeping track of whether the opener
has ever tried to acquire the lock, and not try to acquire it at
close/release when it had never previously been acquired. The patch
below implements this.
3. The drm module AGP support code keeps track of a "handle" for
allocated AGP regions. It uses the memory address returned from
the allocation for this purpose. This would normally be unique,
but for the i810 driver case where "DCACHE" memory is "allocated".
In the DCACHE case, the allocated memory is freed immediately (since
DCACHE doesn't come from the system memory pool), and the next
allocation often ends up getting the same memory address, and thus
the same "handle". This showed up as a problem when the unbind/rebind
code was added.
The user-level agpgart interfaces use a "key" value to uniquely
identify allocated AGP regions. I don't know why the DRM module
doesn't do the same, since the key is guaranteed to be unique.
The patch below changes the handle to be the "key" value.
I'm wary of making changes like this so close to the 4.3 release, but
I would like to see this problem fixed in 4.3. Does anyone see any
potential problems with the attached patch?
David
--
David Dawes
Release Engineer/Architect The XFree86 Project
www.XFree86.org/~dawes
Index: drivers/i810/i810.h
===================================================================
RCS file: /home/x-cvs/xc/programs/Xserver/hw/xfree86/drivers/i810/i810.h,v
retrieving revision 1.37
diff -u -r1.37 i810.h
--- drivers/i810/i810.h 2002/12/10 01:27:04 1.37
+++ drivers/i810/i810.h 2003/02/14 20:00:33
@@ -264,6 +264,9 @@
extern Bool I810DRIFinishScreenInit(ScreenPtr pScreen);
extern Bool I810InitDma(ScrnInfoPtr pScrn);
extern Bool I810CleanupDma(ScrnInfoPtr pScrn);
+extern Bool I810DRILeave(ScrnInfoPtr pScrn);
+extern Bool I810DRIEnter(ScrnInfoPtr pScrn);
+
#define I810PTR(p) ((I810Ptr)((p)->driverPrivate))
#define I810REGPTR(p) (&(I810PTR(p)->ModeReg))
Index: drivers/i810/i810_dri.c
===================================================================
RCS file: /home/x-cvs/xc/programs/Xserver/hw/xfree86/drivers/i810/i810_dri.c,v
retrieving revision 1.33
diff -u -r1.33 i810_dri.c
--- drivers/i810/i810_dri.c 2002/12/10 01:27:04 1.33
+++ drivers/i810/i810_dri.c 2003/02/14 20:00:33
@@ -1218,3 +1218,84 @@
pI810->AccelInfoRec->NeedToSync = TRUE;
}
+
+Bool
+I810DRILeave(ScrnInfoPtr pScrn)
+{
+ I810Ptr pI810 = I810PTR(pScrn);
+
+ if (pI810->directRenderingEnabled) {
+ if (pI810->dcacheHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->dcacheHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->backHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->backHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->zHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->zHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->sysmemHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->sysmemHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->xvmcHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->xvmcHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->cursorHandle != 0)
+ if (drmAgpUnbind(pI810->drmSubFD, pI810->cursorHandle) != 0) {
+ xf86DrvMsg(pScrn->scrnIndex, X_ERROR,"%s\n",strerror(errno));
+ return FALSE;
+ }
+ if (pI810->agpAcquired == TRUE)
+ drmAgpRelease(pI810->drmSubFD);
+ pI810->agpAcquired = FALSE;
+ }
+ return TRUE;
+}
+
+Bool
+I810DRIEnter(ScrnInfoPtr pScrn)
+{
+ I810Ptr pI810 = I810PTR(pScrn);
+
+ if (pI810->directRenderingEnabled) {
+
+ if (pI810->agpAcquired == FALSE)
+ drmAgpAcquire(pI810->drmSubFD);
+ pI810->agpAcquired = TRUE;
+ if (pI810->dcacheHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->dcacheHandle,
+ pI810->DepthOffset) != 0)
+ return FALSE;
+ if (pI810->backHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->backHandle,
+ pI810->BackOffset) != 0)
+ return FALSE;
+ if (pI810->zHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->zHandle,
+ pI810->DepthOffset) != 0)
+ return FALSE;
+ if (pI810->sysmemHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->sysmemHandle,
+ 0) != 0)
+ return FALSE;
+ if (pI810->xvmcHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->xvmcHandle,
+ pI810->MC.Start) != 0)
+ return FALSE;
+ if (pI810->cursorHandle != 0)
+ if (drmAgpBind(pI810->drmSubFD, pI810->cursorHandle,
+ pI810->CursorStart) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
Index: drivers/i810/i810_driver.c
===================================================================
RCS file: /home/x-cvs/xc/programs/Xserver/hw/xfree86/drivers/i810/i810_driver.c,v
retrieving revision 1.79
diff -u -r1.79 i810_driver.c
--- drivers/i810/i810_driver.c 2003/02/14 17:12:42 1.79
+++ drivers/i810/i810_driver.c 2003/02/14 20:00:21
@@ -2218,6 +2218,9 @@
return FALSE;
#ifdef XF86DRI
+ if (!I810DRIEnter(pScrn))
+ return FALSE;
+
if (pI810->directRenderingEnabled) {
if (I810_DEBUG & DEBUG_VERBOSE_DRI)
ErrorF("calling dri unlock\n");
@@ -2260,6 +2263,11 @@
if (!I810UnbindGARTMemory(pScrn))
return;
+
+#ifdef XF86DRI
+ if (!I810DRILeave(pScrn))
+ return;
+#endif
vgaHWLock(hwp);
}
Index: os-support/linux/drm/kernel/drmP.h
===================================================================
RCS file:
/home/x-cvs/xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v
retrieving revision 1.27
diff -u -r1.27 drmP.h
--- os-support/linux/drm/kernel/drmP.h 2003/01/29 23:00:43 1.27
+++ os-support/linux/drm/kernel/drmP.h 2003/02/14 19:53:44
@@ -418,6 +418,7 @@
struct drm_file *prev;
struct drm_device *dev;
int remove_auth_on_close;
+ unsigned long lock_count;
} drm_file_t;
@@ -484,7 +485,7 @@
#if __REALLY_HAVE_AGP
typedef struct drm_agp_mem {
- unsigned long handle;
+ int key;
agp_memory *memory;
unsigned long bound; /* address */
int pages;
Index: os-support/linux/drm/kernel/drm_agpsupport.h
===================================================================
RCS file:
/home/x-cvs/xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm_agpsupport.h,v
retrieving revision 1.7
diff -u -r1.7 drm_agpsupport.h
--- os-support/linux/drm/kernel/drm_agpsupport.h 2002/12/16 16:19:28 1.7
+++ os-support/linux/drm/kernel/drm_agpsupport.h 2003/02/14 20:01:18
@@ -147,7 +147,7 @@
return -ENOMEM;
}
- entry->handle = (unsigned long)memory->memory;
+ entry->key = memory->key;
entry->memory = memory;
entry->bound = 0;
entry->pages = pages;
@@ -156,7 +156,7 @@
if (dev->agp->memory) dev->agp->memory->prev = entry;
dev->agp->memory = entry;
- request.handle = entry->handle;
+ request.handle = entry->key;
request.physical = memory->physical;
if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) {
@@ -175,7 +175,7 @@
drm_agp_mem_t *entry;
for (entry = dev->agp->memory; entry; entry = entry->next) {
- if (entry->handle == handle) return entry;
+ if (entry->key == handle) return entry;
}
return NULL;
}
@@ -187,6 +187,7 @@
drm_device_t *dev = priv->dev;
drm_agp_binding_t request;
drm_agp_mem_t *entry;
+ int ret;
if (!dev->agp || !dev->agp->acquired) return -EINVAL;
if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request)))
@@ -194,7 +195,10 @@
if (!(entry = DRM(agp_lookup_entry)(dev, request.handle)))
return -EINVAL;
if (!entry->bound) return -EINVAL;
- return DRM(unbind_agp)(entry->memory);
+ ret = DRM(unbind_agp)(entry->memory);
+ if (ret == 0)
+ entry->bound = 0;
+ return ret;
}
int DRM(agp_bind)(struct inode *inode, struct file *filp,
Index: os-support/linux/drm/kernel/drm_drv.h
===================================================================
RCS file:
/home/x-cvs/xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm_drv.h,v
retrieving revision 1.10
diff -u -r1.10 drm_drv.h
--- os-support/linux/drm/kernel/drm_drv.h 2002/11/25 14:05:04 1.10
+++ os-support/linux/drm/kernel/drm_drv.h 2003/02/14 17:32:02
@@ -768,7 +768,7 @@
DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n",
current->pid, (long)dev->device, dev->open_count );
- if ( dev->lock.hw_lock &&
+ if ( priv->lock_count && dev->lock.hw_lock &&
_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) &&
dev->lock.pid == current->pid ) {
DRM_DEBUG( "Process %d dead, freeing lock for context %d\n",
@@ -786,7 +786,7 @@
server. */
}
#if __HAVE_RELEASE
- else if ( dev->lock.hw_lock ) {
+ else if ( priv->lock_count && dev->lock.hw_lock ) {
/* The lock is required to reclaim buffers */
DECLARE_WAITQUEUE( entry, current );
add_wait_queue( &dev->lock.lock_queue, &entry );
@@ -932,6 +932,8 @@
dev->lck_start = start = get_cycles();
#endif
+
+ ++priv->lock_count;
if ( copy_from_user( &lock, (drm_lock_t *)arg, sizeof(lock) ) )
return -EFAULT;
Index: os-support/linux/drm/kernel/drm_fops.h
===================================================================
RCS file:
/home/x-cvs/xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm_fops.h,v
retrieving revision 1.7
diff -u -r1.7 drm_fops.h
--- os-support/linux/drm/kernel/drm_fops.h 2002/11/25 14:05:04 1.7
+++ os-support/linux/drm/kernel/drm_fops.h 2003/02/14 17:32:26
@@ -57,6 +57,7 @@
priv->dev = dev;
priv->ioctl_count = 0;
priv->authenticated = capable(CAP_SYS_ADMIN);
+ priv->lock_count = 0;
down(&dev->struct_sem);
if (!dev->file_last) {