
From: Brice Goglin <Brice.Goglin@ens-lyon.org>

The bug appeared in -mm when the multi-bridge AGP stuff was merged
(2.6.10-mm3).  It is still here in 2.6.12-rc1.

Here's the scenario I think I'm seeing:

agpioc_acquire_wrap is called, it increments the agp_in_use.  Then (before
agpioc_release_wrap happens), drm_agp_init is called (I don't know how). 
drm_agp_init uses agp_backend_acquire which fails because agp_in_use is
non-null (hold by agpioc_acquire_wrap).

The multi-bridge AGP patch actually changed drm_agp_init by adding
agp_backend_acquire/release around agp_copy_info.  It is why drm_agp_init
fails now while it worked before.

I don't think we need to "acquire" it during agp_copy_info.  Why don't we
just get a pointer to the bridge instead ?  (is there any chance this
bridge gets deleted during drm_agp_init ?) That's what the attached patch
implements on top of 2.6.12-rc1.

I chose to add a new agp_backend_find() function, but we might also
directly call agp_find_bridge() from drm_agp_init().  I don't know what's
the best.

Signed-off-by: Brice Goglin <Brice.Goglin@ens-lyon.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/char/agp/backend.c        |    8 +++++++-
 25-akpm/drivers/char/drm/drm_agpsupport.c |    3 +--
 25-akpm/include/linux/agp_backend.h       |    1 +
 3 files changed, 9 insertions(+), 3 deletions(-)

diff -puN drivers/char/agp/backend.c~fix-agp_backend-usage-in-drm_agp_init drivers/char/agp/backend.c
--- 25/drivers/char/agp/backend.c~fix-agp_backend-usage-in-drm_agp_init	2005-03-18 23:54:18.000000000 -0800
+++ 25-akpm/drivers/char/agp/backend.c	2005-03-18 23:54:18.000000000 -0800
@@ -58,6 +58,12 @@ LIST_HEAD(agp_bridges);
 EXPORT_SYMBOL(agp_bridge);
 EXPORT_SYMBOL(agp_bridges);
 
+struct agp_bridge_data *agp_backend_find(struct pci_dev *pdev)
+{
+	return agp_find_bridge(pdev);
+}
+EXPORT_SYMBOL(agp_backend_find);
+
 /**
  *	agp_backend_acquire  -  attempt to acquire an agp backend.
  *
@@ -66,7 +72,7 @@ struct agp_bridge_data *agp_backend_acqu
 {
 	struct agp_bridge_data *bridge;
 
-	bridge = agp_find_bridge(pdev);
+	bridge = agp_backend_find(pdev);
 
 	if (!bridge)
 		return NULL;
diff -puN drivers/char/drm/drm_agpsupport.c~fix-agp_backend-usage-in-drm_agp_init drivers/char/drm/drm_agpsupport.c
--- 25/drivers/char/drm/drm_agpsupport.c~fix-agp_backend-usage-in-drm_agp_init	2005-03-18 23:54:18.000000000 -0800
+++ 25-akpm/drivers/char/drm/drm_agpsupport.c	2005-03-18 23:54:18.000000000 -0800
@@ -387,12 +387,11 @@ drm_agp_head_t *drm_agp_init(drm_device_
 	if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
 		return NULL;
 	memset((void *)head, 0, sizeof(*head));
-	if (!(head->bridge = agp_backend_acquire(dev->pdev))) {
+	if (!(head->bridge = agp_backend_find(dev->pdev))) {
 		drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS);
 		return NULL;
 	}
 	agp_copy_info(head->bridge, &head->agp_info);
-	agp_backend_release(head->bridge);
 	if (head->agp_info.chipset == NOT_SUPPORTED) {
 		drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS);
 		return NULL;
diff -puN include/linux/agp_backend.h~fix-agp_backend-usage-in-drm_agp_init include/linux/agp_backend.h
--- 25/include/linux/agp_backend.h~fix-agp_backend-usage-in-drm_agp_init	2005-03-18 23:54:18.000000000 -0800
+++ 25-akpm/include/linux/agp_backend.h	2005-03-18 23:54:18.000000000 -0800
@@ -100,6 +100,7 @@ extern int agp_copy_info(struct agp_brid
 extern int agp_bind_memory(struct agp_memory *, off_t);
 extern int agp_unbind_memory(struct agp_memory *);
 extern void agp_enable(struct agp_bridge_data *, u32);
+extern struct agp_bridge_data *agp_backend_find(struct pci_dev *);
 extern struct agp_bridge_data *agp_backend_acquire(struct pci_dev *);
 extern void agp_backend_release(struct agp_bridge_data *);
 
_
