From f276c845bde4c712aa383540a2dd2055ecc00031 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 13 Feb 2008 12:12:52 +1000 Subject: drm: re-write minor number allocation to use an idr. Fixup the minor number allocation scheme to use an idr and move the control nodes up higher. --- linux-core/drmP.h | 13 +++-- linux-core/drm_drv.c | 50 ++++++++--------- linux-core/drm_fops.c | 30 +++++------ linux-core/drm_proc.c | 2 +- linux-core/drm_stub.c | 143 ++++++++++++++++++++++++++++++++----------------- linux-core/drm_sysfs.c | 4 +- 6 files changed, 141 insertions(+), 101 deletions(-) (limited to 'linux-core') diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 1c815c5f..afc16f5f 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -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) */ diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 7177f171..e9955239 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -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); diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 28aaeb1f..344d90e1 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -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); diff --git a/linux-core/drm_proc.c b/linux-core/drm_proc.c index 8f26726c..b28d8238 100644 --- a/linux-core/drm_proc.c +++ b/linux-core/drm_proc.c @@ -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); } diff --git a/linux-core/drm_stub.c b/linux-core/drm_stub.c index cee00c5e..5a39afb3 100644 --- a/linux-core/drm_stub.c +++ b/linux-core/drm_stub.c @@ -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; } diff --git a/linux-core/drm_sysfs.c b/linux-core/drm_sysfs.c index 114e355e..cd03da8a 100644 --- a/linux-core/drm_sysfs.c +++ b/linux-core/drm_sysfs.c @@ -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) { -- cgit v1.2.3