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) {

Reply via email to