Hello Thomas,

Thanks for your positive feedback.

On Sun, Aug 14, 2005 at 09:38:45AM +0200, Thomas Hellstr?m wrote:
> >01_via_unichrome_pro
> > Trivial patch to make the VIA driver recognize my PCI id as
> > a Unichrome Pro chipset. This makes IRQ handling work properly
> > and avoids the kernel message
> >   irq 201: nobody cared (try booting with the "irqpoll" option)

> I have the same problem on my K8M800/K8N800 computers, but when a stray 
> interrupt occurs, nothing is seen in the Unichrome IRQ status register, 
> and returning  IRQ_HANDLED will make the unichrome issue about 300000 
> interrupts per second. I've asked VIA about this and their first 
> response is that the IRQ functionality was never verified on chipsets 
> not having video capture functionality.

I was not aware of this, and unfortunately you seem to be right. I am
getting 300000 interrupts per second with this patch. Completely missed
that, although I did have the impression that my system was a bit
slow overall. Some of these interrupts are genuine VBLANKs, but I
can't figure out how to disable the spurious interrupts.

> Patch 3: I agree that there is checking needed for the returned index 
> values, but can't this be solved using range checking on the kernel pointers,
> or perhaps store the pointers in a hash table, the content of which is
> verified before a memblock is freed?
> It seems very inefficient to loop through the block map (up to 5000 
> blocks) on each allocation.

My solution is not less efficient than the old situation. The old mm code
can do fast mem_alloc() but will loop through all allocated blocks on
mem_free() (twice). My current solution will loop on mem_alloc() but has
a faster mem_free().

If you are concerned, I can use hashing to speed up mem_alloc(). I did
this originally but then thought it not worthwhile. A possible approach
is attached as 03a_via_mm_cleanup. This will of course turn slow again
when the number of allocated blocks passes a certain treshold.

> BTW How does the current solution  interfere with x86-64?
> Here it works nicely except the kernel crashes when it tries to free 
> unfreed memory blocks in final_context, but I haven't been able to track 
> down the cause yet.

The old solution passes a kernel pointer to userspace as a unsigned long.
This should work in i386 as well as pure x86_64, but not in x86_64 with
32-bit compatibility. To be honest, I did not even test it; it simply
cannot work.

> Patch 4: An oversight in the initial code. It seems correct except for 
> the DMA_INIT ioctl where the privilege checking is done within the IOCTL 
> code.

Right. I missed the check in the ioctl.

Bye,
  Joris.
diff -urN -U 5 drm.prev/shared-core/via_mm.c drm/shared-core/via_mm.c
--- drm.prev/shared-core/via_mm.c       2005-08-14 11:10:23.000000000 +0200
+++ drm/shared-core/via_mm.c    2005-08-14 11:19:24.000000000 +0200
@@ -23,52 +23,131 @@
  */
 #include "drmP.h"
 #include "via_drm.h"
 #include "via_drv.h"
 #include "via_ds.h"
-#include "via_mm.h"
 
 #define MAX_CONTEXT 100
+#define MAX_MEMBLOCK_INDEX 5003
+
+/* Structure which maps indices to PMemBlock pointers.
+   index_base is used to make indices globally unique among
+   multiple mem_block_map_t structures, and to avoid ever using
+   the special value 0 as an index. */
+typedef struct {
+       unsigned int index_base;
+       PMemBlock m[MAX_MEMBLOCK_INDEX];
+} mem_block_map_t;
 
 typedef struct {
        int used;
        int context;
-       set_t *sets[2];         /* 0 for frame buffer, 1 for AGP , 2 for System 
*/
+       mem_block_map_t *map[2]; /* 0 for frame buffer, 1 for AGP */
 } via_context_t;
 
 static via_context_t global_ppriv[MAX_CONTEXT];
 
 static int via_agp_alloc(drm_via_mem_t * mem);
 static int via_agp_free(drm_via_mem_t * mem);
 static int via_fb_alloc(drm_via_mem_t * mem);
 static int via_fb_free(drm_via_mem_t * mem);
 
-static int add_alloc_set(int context, int type, unsigned int val)
+/*
+ * Allocate a mem_block_map_t and initialize it to make all indices
+ * point to NULL. index_base must be non-zero
+ */
+static mem_block_map_t * via_mem_block_map_init(unsigned int index_base)
 {
-       int i, retval = 0;
+       mem_block_map_t *map;
+       int i;
 
-       for (i = 0; i < MAX_CONTEXT; i++) {
-               if (global_ppriv[i].used && global_ppriv[i].context == context) 
{
-                       retval = via_setAdd(global_ppriv[i].sets[type], val);
-                       break;
-               }
+       map = drm_alloc(sizeof(mem_block_map_t), DRM_MEM_DRIVER);
+       if (map) {
+               map->index_base = index_base;
+               for (i = 0; i < MAX_MEMBLOCK_INDEX; i++)
+                       map->m[i] = NULL;
        }
-
-       return retval;
+       return map;
 }
 
-static int del_alloc_set(int context, int type, unsigned int val)
+/*
+ * Destroy a mem_block_map_t and release allocated memory.
+ */
+static void via_mem_block_map_destroy(mem_block_map_t *map)
 {
-       int i, retval = 0;
+       drm_free(map, sizeof(mem_block_map_t), DRM_MEM_DRIVER);
+}
 
-       for (i = 0; i < MAX_CONTEXT; i++)
-               if (global_ppriv[i].used && global_ppriv[i].context == context) 
{
-                       retval = via_setDel(global_ppriv[i].sets[type], val);
-                       break;
+
+/*
+ * Add a pointer to a mem_block_map_t and return the new index,
+ * or return 0 if all slots are in use.
+ */
+static unsigned int via_mem_block_map_add(mem_block_map_t *map, PMemBlock 
block)
+{
+       int h = (((unsigned long)block) >> 4) %
+                 ((unsigned long)MAX_MEMBLOCK_INDEX);
+       int i;
+       for (i = h; i < MAX_MEMBLOCK_INDEX; i++)
+               if (!map->m[i]) {
+                       map->m[i] = block;
+                       return i + map->index_base;
                }
+       for (i = 0; i < h; i++)
+               if (!map->m[i]) {
+                       map->m[i] = block;
+                       return i + map->index_base;
+               }
+       return 0;
+}
 
-       return retval;
+/*
+ * Remove a pointer from a given index in a mem_block_map_t and return
+ * the removed pointer, or return NULL if no pointer was present.
+ */
+static PMemBlock via_mem_block_map_remove(mem_block_map_t *map,
+                                          unsigned int index)
+{
+       PMemBlock block;
+       if (index < map->index_base)
+               return NULL;
+       index -= map->index_base;
+       if (index >= MAX_MEMBLOCK_INDEX)
+               return NULL;
+       block = map->m[index];
+       map->m[index] = NULL;
+       return block;
+}
+
+/*
+ * Add this block pointer to the allocation map of the specified context
+ * and return the block index, or return 0 for failure.
+ */
+static unsigned int add_alloc_map(int context, int type, PMemBlock block)
+{
+       int i;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               via_context_t *ctx = &global_ppriv[i];
+               if (ctx->used && ctx->context == context)
+                       return via_mem_block_map_add(ctx->map[type], block);
+       }
+       return 0;
+}
+
+/*
+ * Lookup and remove the specified block index from the context allocation map,
+ * and return the corresponding block pointer, or return NULL for not found.
+ */
+static PMemBlock del_alloc_map(int context, int type, unsigned int index)
+{
+       int i;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               via_context_t *ctx = &global_ppriv[i];
+               if (ctx->used && ctx->context == context)
+                       return via_mem_block_map_remove(ctx->map[type], index);
+       }
+       return NULL;
 }
 
 /* agp memory management */
 static memHeap_t *AgpHeap = NULL;
 
@@ -107,32 +186,37 @@
        int i;
 
        for (i = 0; i < MAX_CONTEXT; i++)
                if (global_ppriv[i].used &&
                    (global_ppriv[i].context == context))
-                       break;
+                       return 1;
 
-       if (i >= MAX_CONTEXT) {
-               for (i = 0; i < MAX_CONTEXT; i++) {
-                       if (!global_ppriv[i].used) {
-                               global_ppriv[i].context = context;
-                               global_ppriv[i].used = 1;
-                               global_ppriv[i].sets[0] = via_setInit();
-                               global_ppriv[i].sets[1] = via_setInit();
-                               DRM_DEBUG("init allocation set, socket=%d,"
-                                         " context = %d\n", i, context);
-                               break;
+       for (i = 0; i < MAX_CONTEXT; i++) {
+               if (!global_ppriv[i].used) {
+                       global_ppriv[i].map[0] = 
via_mem_block_map_init(2*i*MAX_MEMBLOCK_INDEX + 1);
+                       global_ppriv[i].map[1] = 
via_mem_block_map_init((2*i+1)*MAX_MEMBLOCK_INDEX + 1);
+                       if ( global_ppriv[i].map[0] == NULL ||
+                            global_ppriv[i].map[1] == NULL ) {
+                               if (global_ppriv[i].map[0] != NULL)
+                                       
via_mem_block_map_destroy(global_ppriv[i].map[0]);
+                               if (global_ppriv[i].map[1] != NULL)
+                                       
via_mem_block_map_destroy(global_ppriv[i].map[1]);
+                               DRM_DEBUG("init allocation set failed,"
+                                         " no memory, context=%d\n", context);
+                               return 0;
                        }
-               }
-
-               if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) ||
-                   (global_ppriv[i].sets[1] == NULL)) {
-                       return 0;
+                       global_ppriv[i].context = context;
+                       global_ppriv[i].used = 1;
+                       DRM_DEBUG("init allocation set, socket=%d,"
+                                 " context = %d\n", i, context);
+                       return 1;
                }
        }
 
-       return 1;
+       DRM_DEBUG("init allocation set failed, no free socket, context=%d\n",
+                 context);
+       return 0;
 }
 
 int via_final_context(struct drm_device *dev, int context)
 {      
         int i;
@@ -142,35 +226,36 @@
                if (global_ppriv[i].used &&
                    (global_ppriv[i].context == context))
                        break;
 
        if (i < MAX_CONTEXT) {
-               set_t *set;
-               ITEM_TYPE item;
-               int retval;
+               mem_block_map_t *map;
+               int index;
 
                DRM_DEBUG("find socket %d, context = %d\n", i, context);
 
                /* Video Memory */
-               set = global_ppriv[i].sets[0];
-               retval = via_setFirst(set, &item);
-               while (retval) {
-                       DRM_DEBUG("free video memory 0x%lx\n", item);
-                       via_mmFreeMem((PMemBlock) item);
-                       retval = via_setNext(set, &item);
+               map = global_ppriv[i].map[0];
+               for (index = 0; index < MAX_MEMBLOCK_INDEX; index++) {
+                       PMemBlock item = map->m[index];
+                       if (item) {
+                               DRM_DEBUG("free video memory %p\n", item);
+                               via_mmFreeMem(item);
+                       }
                }
-               via_setDestroy(set);
+               via_mem_block_map_destroy(map);
 
                /* AGP Memory */
-               set = global_ppriv[i].sets[1];
-               retval = via_setFirst(set, &item);
-               while (retval) {
-                       DRM_DEBUG("free agp memory 0x%lx\n", item);
-                       via_mmFreeMem((PMemBlock) item);
-                       retval = via_setNext(set, &item);
+               map = global_ppriv[i].map[1];
+               for (index = 0; index < MAX_MEMBLOCK_INDEX; index++) {
+                       PMemBlock item = map->m[index];
+                       if (item) {
+                               DRM_DEBUG("free agp memory %p\n", item);
+                               via_mmFreeMem(item);
+                       }
                }
-               via_setDestroy(set);
+               via_mem_block_map_destroy(map);
                global_ppriv[i].used = 0;
        }
        via_release_futex(dev_priv, context); 
        
                        
@@ -215,77 +300,67 @@
        return -EFAULT;
 }
 
 static int via_fb_alloc(drm_via_mem_t * mem)
 {
-       drm_via_mm_t fb;
        PMemBlock block;
        int retval = 0;
 
        if (!FBHeap)
                return -1;
 
-       fb.size = mem->size;
-       fb.context = mem->context;
-
-       block = via_mmAllocMem(FBHeap, fb.size, 5, 0);
+       block = via_mmAllocMem(FBHeap, mem->size, 5, 0);
        if (block) {
-               fb.offset = block->ofs;
-               fb.free = (unsigned long)block;
-               if (!add_alloc_set(fb.context, VIDEO, fb.free)) {
+               int index = add_alloc_map(mem->context, VIDEO, block);
+               if (!index) {
                        DRM_DEBUG("adding to allocation set fails\n");
-                       via_mmFreeMem((PMemBlock) fb.free);
+                       via_mmFreeMem(block);
                        retval = -1;
+               } else {
+                       mem->offset = block->ofs;
+                       mem->index = index;
                }
        } else {
-               fb.offset = 0;
-               fb.size = 0;
-               fb.free = 0;
+               mem->offset = 0;
+               mem->index = 0;
                retval = -1;
        }
 
-       mem->offset = fb.offset;
-       mem->index = fb.free;
-
-       DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size,
-                 (int)fb.offset);
+       DRM_DEBUG("alloc fb, size = %d, offset = %d\n",
+                 mem->size, (unsigned int)mem->offset);
 
        return retval;
 }
 
 static int via_agp_alloc(drm_via_mem_t * mem)
 {
-       drm_via_mm_t agp;
        PMemBlock block;
        int retval = 0;
 
        if (!AgpHeap)
                return -1;
 
-       agp.size = mem->size;
-       agp.context = mem->context;
-
-       block = via_mmAllocMem(AgpHeap, agp.size, 5, 0);
+       block = via_mmAllocMem(AgpHeap, mem->size, 5, 0);
        if (block) {
-               agp.offset = block->ofs;
-               agp.free = (unsigned long)block;
-               if (!add_alloc_set(agp.context, AGP, agp.free)) {
+               int index = add_alloc_map(mem->context, AGP, block);
+               if (!index) {
                        DRM_DEBUG("adding to allocation set fails\n");
-                       via_mmFreeMem((PMemBlock) agp.free);
+                       via_mmFreeMem(block);
                        retval = -1;
+               } else {
+                       mem->offset = block->ofs;
+                       mem->index = index;
                }
        } else {
-               agp.offset = 0;
-               agp.size = 0;
-               agp.free = 0;
+               mem->offset = 0;
+               mem->index = 0;
+               /* should we set retval = -1 ?? */
        }
 
-       mem->offset = agp.offset;
-       mem->index = agp.free;
+       DRM_DEBUG("alloc agp, size = %d, offset = %d\n",
+                 mem->size, (unsigned int)mem->offset);
 
-       DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size,
-                 (unsigned int)agp.offset);
        return retval;
 }
 
 int via_mem_free(DRM_IOCTL_ARGS)
 {
@@ -309,53 +384,48 @@
        return -EFAULT;
 }
 
 static int via_fb_free(drm_via_mem_t * mem)
 {
-       drm_via_mm_t fb;
+       PMemBlock block;
        int retval = 0;
 
        if (!FBHeap) {
                return -1;
        }
 
-       fb.free = mem->index;
-       fb.context = mem->context;
-
-       if (!fb.free) {
+       if (!mem->index) {
                return -1;
 
        }
 
-       via_mmFreeMem((PMemBlock) fb.free);
-
-       if (!del_alloc_set(fb.context, VIDEO, fb.free)) {
+       block = del_alloc_map(mem->context, VIDEO, mem->index);
+       if (!block) {
                retval = -1;
+       } else {
+               via_mmFreeMem(block);
        }
 
-       DRM_DEBUG("free fb, free = %ld\n", fb.free);
+       DRM_DEBUG("free fb, index = %ld\n", mem->index);
 
        return retval;
 }
 
 static int via_agp_free(drm_via_mem_t * mem)
 {
-       drm_via_mm_t agp;
-
+       PMemBlock block;
        int retval = 0;
 
-       agp.free = mem->index;
-       agp.context = mem->context;
-
-       if (!agp.free)
+       if (!mem->index)
                return -1;
 
-       via_mmFreeMem((PMemBlock) agp.free);
-
-       if (!del_alloc_set(agp.context, AGP, agp.free)) {
+       block = del_alloc_map(mem->context, AGP, mem->index);
+       if (!block) {
                retval = -1;
+       } else {
+               via_mmFreeMem(block);
        }
 
-       DRM_DEBUG("free agp, free = %ld\n", agp.free);
+       DRM_DEBUG("free agp, index = %ld\n", mem->index);
 
        return retval;
 }
diff -urN -U 5 drm.prev/shared-core/via_mm.h drm/shared-core/via_mm.h
--- drm.prev/shared-core/via_mm.h       2005-08-14 11:10:23.000000000 +0200
+++ drm/shared-core/via_mm.h    1970-01-01 01:00:00.000000000 +0100
@@ -1,40 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sub license,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#ifndef _via_drm_mm_h_
-#define _via_drm_mm_h_
-
-typedef struct {
-       unsigned int context;
-       unsigned int size;
-       unsigned long offset;
-       unsigned long free;
-} drm_via_mm_t;
-
-typedef struct {
-       unsigned int size;
-       unsigned long handle;
-       void *virtual;
-} drm_via_dma_t;
-
-#endif

Reply via email to