drm: re-write minor number allocation to use an idr.
authorDave Airlie <airlied@redhat.com>
Wed, 13 Feb 2008 02:12:52 +0000 (12:12 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 13 Feb 2008 02:12:52 +0000 (12:12 +1000)
Fixup the minor number allocation scheme to use an idr and move the control
nodes up higher.

linux-core/drmP.h
linux-core/drm_drv.c
linux-core/drm_fops.c
linux-core/drm_proc.c
linux-core/drm_stub.c
linux-core/drm_sysfs.c

index 1c815c5..afc16f5 100644 (file)
@@ -733,11 +733,12 @@ struct drm_driver {
 #define DRM_MINOR_UNASSIGNED 0
 #define DRM_MINOR_CONTROL 1
 #define DRM_MINOR_RENDER 2
+#define DRM_MINOR_GPGPU 3 /* this node is restricted to operations that don't require a master */
 /**
  * DRM minor structure. This structure represents a drm minor number.
  */
 struct drm_minor {
-       int minor;                      /**< Minor device number */
+       int index;                      /**< Minor device number */
        int type;                       /**< Control or render */
        dev_t device;                   /**< Device number for mknod */
        struct device kdev;             /**< Linux device */
@@ -882,8 +883,8 @@ struct drm_device {
        unsigned int agp_buffer_token;
 
        /* minor number for control node */
-       struct drm_minor control;
-       struct drm_minor primary;               /**< primary screen head */
+       struct drm_minor *control;
+       struct drm_minor *primary;              /**< primary screen head */
 
        struct drm_fence_manager fm;
        struct drm_buffer_manager bm;
@@ -1214,13 +1215,15 @@ extern void drm_agp_chipset_flush(struct drm_device *dev);
 extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
                     struct drm_driver *driver);
 extern int drm_put_dev(struct drm_device *dev);
-extern int drm_put_minor(struct drm_minor *minor);
+extern int drm_put_minor(struct drm_minor **minor);
 extern unsigned int drm_debug; /* 1 to enable debug output */
 extern unsigned int drm_minors_limit;
-extern struct drm_minor **drm_minors;
+
 extern struct class *drm_class;
 extern struct proc_dir_entry *drm_proc_root;
 
+extern struct idr drm_minors_idr;
+
 extern drm_local_map_t *drm_getsarea(struct drm_device *dev);
 
                                /* Proc support (drm_proc.h) */
index 7177f17..e995523 100644 (file)
@@ -435,30 +435,29 @@ static void drm_cleanup(struct drm_device * dev)
                DRM_ERROR("Cannot unload module\n");
 }
 
-void drm_exit(struct drm_driver *driver)
+int drm_minors_cleanup(int id, void *ptr, void *data)
 {
-       int i;
-       struct drm_device *dev = NULL;
-       struct drm_minor *minor;
+       struct drm_minor *minor = ptr;
+       struct drm_device *dev;
+       struct drm_driver *driver = data;
+       if (id < 127 || id > 192)
+               return 0;
+
+       dev = minor->dev;
+       if (minor->dev->driver != driver)
+               return 0;
+
+       if (dev)
+               pci_dev_put(dev->pdev);
+       drm_cleanup(dev);
+       return 1;
+}
 
+void drm_exit(struct drm_driver *driver)
+{
        DRM_DEBUG("\n");
        if (drm_fb_loaded) {
-               for (i = 0; i < drm_minors_limit; i++) {
-                       minor = drm_minors[i];
-                       if (!minor)
-                               continue;
-                       if (!minor->dev)
-                               continue;
-                       if (minor->dev->driver != driver)
-                               continue;
-                       dev = minor->dev;
-                       if (dev) {
-                               /* release the pci driver */
-                               if (dev->pdev)
-                                       pci_dev_put(dev->pdev);
-                               drm_cleanup(dev);
-                       }
-               }
+               idr_for_each(&drm_minors_idr, &drm_minors_cleanup, driver);
        } else
                pci_unregister_driver(&driver->pci_driver);
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
@@ -481,6 +480,7 @@ static int __init drm_core_init(void)
        unsigned long avail_memctl_mem;
        unsigned long max_memctl_mem;
 
+       idr_init(&drm_minors_idr);
        si_meminfo(&si);
 
        /*
@@ -502,11 +502,6 @@ static int __init drm_core_init(void)
        drm_init_memctl(avail_memctl_mem/2, avail_memctl_mem*3/4, si.mem_unit);
 
        ret = -ENOMEM;
-       drm_minors_limit =
-           (drm_minors_limit < DRM_MAX_MINOR + 1 ? drm_minors_limit : DRM_MAX_MINOR + 1);
-       drm_minors = drm_calloc(drm_minors_limit, sizeof(*drm_minors), DRM_MEM_STUB);
-       if (!drm_minors)
-               goto err_p1;
 
        if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
                goto err_p1;
@@ -535,7 +530,8 @@ err_p3:
        drm_sysfs_destroy();
 err_p2:
        unregister_chrdev(DRM_MAJOR, "drm");
-       drm_free(drm_minors, sizeof(*drm_minors) * drm_minors_limit, DRM_MEM_STUB);
+
+       idr_destroy(&drm_minors_idr);
 err_p1:
        return ret;
 }
@@ -547,7 +543,7 @@ static void __exit drm_core_exit(void)
 
        unregister_chrdev(DRM_MAJOR, "drm");
 
-       drm_free(drm_minors, sizeof(*drm_minors) * drm_minors_limit, DRM_MEM_STUB);
+       idr_destroy(&drm_minors_idr);
 }
 
 module_init(drm_core_init);
index 28aaeb1..344d90e 100644 (file)
@@ -128,16 +128,15 @@ static int drm_setup(struct drm_device * dev)
 int drm_open(struct inode *inode, struct file *filp)
 {
        struct drm_device *dev = NULL;
-       int minor = iminor(inode);
+       int minor_id = iminor(inode);
+       struct drm_minor *minor;
        int retcode = 0;
 
-       if (!((minor >= 0) && (minor < drm_minors_limit)))
+       minor = idr_find(&drm_minors_idr, minor_id);
+       if (!minor)
                return -ENODEV;
 
-       if (!drm_minors[minor])
-               return -ENODEV;
-
-       if (!(dev = drm_minors[minor]->dev))
+       if (!(dev = minor->dev))
                return -ENODEV;
 
        retcode = drm_open_helper(inode, filp, dev);
@@ -176,19 +175,18 @@ EXPORT_SYMBOL(drm_open);
 int drm_stub_open(struct inode *inode, struct file *filp)
 {
        struct drm_device *dev = NULL;
-       int minor = iminor(inode);
+       struct drm_minor *minor;
+       int minor_id = iminor(inode);
        int err = -ENODEV;
        const struct file_operations *old_fops;
 
        DRM_DEBUG("\n");
 
-       if (!((minor >= 0) && (minor < drm_minors_limit)))
+       minor = idr_find(&drm_minors_idr, minor_id);
+       if (!minor)
                return -ENODEV;
-
-       if (!drm_minors[minor])
-               return -ENODEV;
-
-       if (!(dev = drm_minors[minor]->dev))
+       
+       if (!(dev = minor->dev))
                return -ENODEV;
 
        old_fops = filp->f_op;
@@ -233,7 +231,7 @@ static int drm_cpu_valid(void)
 static int drm_open_helper(struct inode *inode, struct file *filp,
                           struct drm_device * dev)
 {
-       int minor = iminor(inode);
+       int minor_id = iminor(inode);
        struct drm_file *priv;
        int ret;
        int i, j;
@@ -243,7 +241,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        if (!drm_cpu_valid())
                return -EINVAL;
 
-       DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
+       DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor_id);
 
        priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
        if (!priv)
@@ -254,7 +252,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        priv->filp = filp;
        priv->uid = current->euid;
        priv->pid = current->pid;
-       priv->minor = drm_minors[minor];
+       priv->minor = idr_find(&drm_minors_idr, minor_id);
        priv->ioctl_count = 0;
        /* for compatibility root is always authenticated */
        priv->authenticated = capable(CAP_SYS_ADMIN);
index 8f26726..b28d823 100644 (file)
@@ -535,7 +535,7 @@ static int drm__clients_info(char *buf, char **start, off_t offset,
        list_for_each_entry(priv, &dev->filelist, lhead) {
                DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
                               priv->authenticated ? 'y' : 'n',
-                              priv->minor->minor,
+                              priv->minor->index,
                               priv->pid,
                               priv->uid, priv->magic, priv->ioctl_count);
        }
index cee00c5..5a39afb 100644 (file)
@@ -50,10 +50,47 @@ MODULE_PARM_DESC(debug, "Enable debug output");
 module_param_named(minors_limit, drm_minors_limit, int, 0444);
 module_param_named(debug, drm_debug, int, 0600);
 
-struct drm_minor **drm_minors;
+struct idr drm_minors_idr;
+
 struct class *drm_class;
 struct proc_dir_entry *drm_proc_root;
 
+static int drm_minor_get_id(struct drm_device *dev, int type)
+{
+       int new_id;
+       int ret;
+       int base = 0, limit = 127;
+
+       if (type == DRM_MINOR_CONTROL) {
+               base += 128;
+               limit = base + 64;
+       } else if (type == DRM_MINOR_GPGPU) {
+               base += 192;
+               limit = base + 64;
+       }       
+
+again:
+       if (idr_pre_get(&drm_minors_idr, GFP_KERNEL) == 0) {
+               DRM_ERROR("Out of memory expanding drawable idr\n");
+               return -ENOMEM;
+       }
+       mutex_lock(&dev->struct_mutex);
+       ret = idr_get_new_above(&drm_minors_idr, NULL,
+                               base, &new_id);
+       mutex_unlock(&dev->struct_mutex);
+       if (ret == -EAGAIN) {
+               goto again;
+       } else if (ret) {
+               return ret;
+       }
+
+       if (new_id >= limit) {
+               idr_remove(&drm_minors_idr, new_id);
+               return -EINVAL;
+       }
+       return new_id;
+}
+
 static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
                           const struct pci_device_id *ent,
                           struct drm_driver *driver)
@@ -160,54 +197,61 @@ error_out_unreg:
  * create the proc init entry via proc_init(). This routines assigns
  * minor numbers to secondary heads of multi-headed cards
  */
-static int drm_get_minor(struct drm_device *dev, struct drm_minor *minor, int type)
+static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type)
 {
-       struct drm_minor **minors = drm_minors;
+       struct drm_minor *new_minor;
        int ret;
-       int index;
+       int minor_id;
 
        DRM_DEBUG("\n");
 
-       for (index = 0; index < drm_minors_limit; index++, minors++) {
-               if (!*minors) {
-
-                       *minor = (struct drm_minor) {
-                               .type = type,
-                               .dev = dev,
-                               .device = MKDEV(DRM_MAJOR, index),
-                               .minor = index,
-                       };
-
-                       if (type == DRM_MINOR_RENDER) {
-                               ret = drm_proc_init(dev, index, drm_proc_root,
-                                                   &minor->dev_root);
-                               if (ret) {
-                                       DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
-                                       goto err_g1;
-                               }
-                       } else
-                               minor->dev_root = NULL;
-
-                       ret = drm_sysfs_device_add(minor);
-                       if (ret) {
-                               printk(KERN_ERR
-                                      "DRM: Error sysfs_device_add.\n");
-                               goto err_g2;
-                       }
-                       *minors = minor;
-
-                       DRM_DEBUG("new minor assigned %d\n", index);
-                       return 0;
+       minor_id = drm_minor_get_id(dev, type);
+       if (minor_id < 0)
+               return minor_id;
+
+       new_minor = kzalloc(sizeof(struct drm_minor), GFP_KERNEL);
+       if (!new_minor) {
+               ret = -ENOMEM;
+               goto err_idr;
+       }
+
+       new_minor->type = type;
+       new_minor->device = MKDEV(DRM_MAJOR, minor_id);
+       new_minor->dev = dev;
+       new_minor->index = minor_id;
+
+       idr_replace(&drm_minors_idr, new_minor, minor_id);
+       
+       if (type == DRM_MINOR_RENDER) {
+               ret = drm_proc_init(dev, minor_id, drm_proc_root,
+                                   &new_minor->dev_root);
+               if (ret) {
+                       DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
+                       goto err_mem;
                }
+       } else
+               new_minor->dev_root = NULL;
+
+       ret = drm_sysfs_device_add(new_minor);
+       if (ret) {
+               printk(KERN_ERR
+                      "DRM: Error sysfs_device_add.\n");
+               goto err_g2;
        }
-       DRM_ERROR("out of minors\n");
-       return -ENOMEM;
+       *minor = new_minor;
+       
+       DRM_DEBUG("new minor assigned %d\n", minor_id);
+       return 0;
+
+
 err_g2:
-       if (minor->type == DRM_MINOR_RENDER)
-               drm_proc_cleanup(index, drm_proc_root, minor->dev_root);
-err_g1:
-       *minor = (struct drm_minor) {
-               .dev = NULL};
+       if (new_minor->type == DRM_MINOR_RENDER)
+               drm_proc_cleanup(minor_id, drm_proc_root, new_minor->dev_root);
+err_mem:
+       kfree(new_minor);
+err_idr:
+       idr_remove(&drm_minors_idr, minor_id);
+       *minor = NULL;
        return ret;
 }
 
@@ -259,7 +303,7 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 
        DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
                 driver->name, driver->major, driver->minor, driver->patchlevel,
-                driver->date, dev->primary.minor);
+                driver->date, dev->primary->index);
 
        return 0;
 
@@ -320,19 +364,18 @@ int drm_put_dev(struct drm_device * dev)
  * last minor released.
  *
  */
-int drm_put_minor(struct drm_minor *minor)
+int drm_put_minor(struct drm_minor **minor_p)
 {
-       int index = minor->minor;
-
-       DRM_DEBUG("release secondary minor %d\n", index);
+       struct drm_minor *minor = *minor_p;
+       DRM_DEBUG("release secondary minor %d\n", minor->index);
 
        if (minor->type == DRM_MINOR_RENDER)
-               drm_proc_cleanup(index, drm_proc_root, minor->dev_root);
+               drm_proc_cleanup(minor->index, drm_proc_root, minor->dev_root);
        drm_sysfs_device_remove(minor);
 
-       *minor = (struct drm_minor) {.type = DRM_MINOR_UNASSIGNED,
-                                    .dev = NULL};
+       idr_remove(&drm_minors_idr, minor->index);
 
-       drm_minors[index] = NULL;
+       kfree(minor);
+       *minor_p = NULL;
        return 0;
 }
index 114e355..cd03da8 100644 (file)
@@ -170,11 +170,11 @@ int drm_sysfs_device_add(struct drm_minor *minor)
        minor->kdev.release = drm_sysfs_device_release;
        minor->kdev.devt = minor->device;
        if (minor->type == DRM_MINOR_CONTROL)
-         minor_str = "controlD%d";
+               minor_str = "controlD%d";
        else
                minor_str = "card%d";
        
-       snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->minor);
+       snprintf(minor->kdev.bus_id, BUS_ID_SIZE, minor_str, minor->index);
 
        err = device_register(&minor->kdev);
        if (err) {