From 45e212d20fdccaf958b194e95a23ad264188c59e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 28 Jan 2014 16:00:35 +0100 Subject: drm: group dev-lifetime related members These members are all managed by DRM-core, lets group them together so they're not split across the whole device. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- include/drm/drmP.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 04a7f31301f8..d6cfca9042fe 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1098,6 +1098,18 @@ struct drm_device { char *devname; /**< For /proc/interrupts */ int if_version; /**< Highest interface version set */ + /** \name Lifetime Management */ + /*@{ */ + struct device *dev; /**< Device structure of bus-device */ + struct drm_driver *driver; /**< DRM driver managing the device */ + void *dev_private; /**< DRM driver private data */ + struct address_space *dev_mapping; /**< Private addr-space just for the device */ + struct drm_minor *control; /**< Control node */ + struct drm_minor *primary; /**< Primary node */ + struct drm_minor *render; /**< Render node */ + atomic_t unplugged; /**< Flag whether dev is dead */ + /*@} */ + /** \name Locks */ /*@{ */ spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ @@ -1171,7 +1183,6 @@ struct drm_device { struct drm_agp_head *agp; /**< AGP data */ - struct device *dev; /**< Device structure */ struct pci_dev *pdev; /**< PCI device structure */ #ifdef __alpha__ struct pci_controller *hose; @@ -1182,17 +1193,11 @@ struct drm_device { struct drm_sg_mem *sg; /**< Scatter gather memory */ unsigned int num_crtcs; /**< Number of CRTCs on this device */ - void *dev_private; /**< device private data */ - struct address_space *dev_mapping; struct drm_sigdata sigdata; /**< For block_all_signals */ sigset_t sigmask; - struct drm_driver *driver; struct drm_local_map *agp_buffer_map; unsigned int agp_buffer_token; - struct drm_minor *control; /**< Control node for card */ - struct drm_minor *primary; /**< render type primary screen head */ - struct drm_minor *render; /**< render node for card */ struct drm_mode_config mode_config; /**< Current mode config */ @@ -1203,8 +1208,6 @@ struct drm_device { struct drm_vma_offset_manager *vma_offset_manager; /*@} */ int switch_power_state; - - atomic_t unplugged; /* device has been unplugged or gone away */ }; #define DRM_SWITCH_POWER_ON 0 -- cgit v1.2.3 From f4aede2e3291896e7cb42755ecc5b6815b6cac97 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 10:18:02 +0100 Subject: drm: skip redundant minor-lookup in open path The drm_open_helper() function is only used internally for drm_open() so we can safely pass in the minor-object directly instead of the minor-id. This way, we avoid the additional minor IDR lookup, which we already do twice in drm_stub_open() and drm_open(). Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_fops.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 7f2af9aca038..6466cb5d8b1f 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -44,7 +44,7 @@ DEFINE_MUTEX(drm_global_mutex); EXPORT_SYMBOL(drm_global_mutex); static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_device * dev); + struct drm_minor *minor); static int drm_setup(struct drm_device * dev) { @@ -110,7 +110,7 @@ int drm_open(struct inode *inode, struct file *filp) filp->f_mapping = dev->dev_mapping; mutex_unlock(&dev->struct_mutex); - retcode = drm_open_helper(inode, filp, dev); + retcode = drm_open_helper(inode, filp, minor); if (retcode) goto err_undo; if (need_setup) { @@ -196,16 +196,16 @@ static int drm_cpu_valid(void) * * \param inode device inode. * \param filp file pointer. - * \param dev device. + * \param minor acquired minor-object. * \return zero on success or a negative number on failure. * * Creates and initializes a drm_file structure for the file private data in \p * filp and add it into the double linked list in \p dev. */ static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_device * dev) + struct drm_minor *minor) { - int minor_id = iminor(inode); + struct drm_device *dev = minor->dev; struct drm_file *priv; int ret; @@ -216,7 +216,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) return -EINVAL; - DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id); + DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -226,11 +226,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->filp = filp; priv->uid = current_euid(); priv->pid = get_pid(task_pid(current)); - priv->minor = idr_find(&drm_minors_idr, minor_id); - if (!priv->minor) { - ret = -ENODEV; - goto out_put_pid; - } + priv->minor = minor; /* for compatibility root is always authenticated */ priv->always_authenticated = capable(CAP_SYS_ADMIN); @@ -336,7 +332,6 @@ out_prime_destroy: drm_prime_destroy_file_private(&priv->prime); if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, priv); -out_put_pid: put_pid(priv->pid); kfree(priv); filp->private_data = NULL; -- cgit v1.2.3 From b9a0d15cc59e896dc6b6c07583157d78fcf72fbb Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 12:30:15 +0100 Subject: drm: remove unused DRM_MINOR_UNASSIGNED This constant is unused, remove it. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- include/drm/drmP.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index d6cfca9042fe..ff20b888d525 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1008,7 +1008,6 @@ struct drm_driver { struct list_head legacy_dev_list; }; -#define DRM_MINOR_UNASSIGNED 0 #define DRM_MINOR_LEGACY 1 #define DRM_MINOR_CONTROL 2 #define DRM_MINOR_RENDER 3 -- cgit v1.2.3 From cb8a239b03608079cbfb784e9ac2f522fe846c29 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 12:31:40 +0100 Subject: drm: turn DRM_MINOR_* into enum Use enum for DRM_MINOR_* constants to avoid hard-coding the IDs. Furthermore, add a DRM_MINOR_CNT so we can perform range-checks in follow-ups. This changes the IDs of the minor-types by -1, but they're not used as indices so this is fine. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- include/drm/drmP.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index ff20b888d525..2c322564ca7d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1008,9 +1008,12 @@ struct drm_driver { struct list_head legacy_dev_list; }; -#define DRM_MINOR_LEGACY 1 -#define DRM_MINOR_CONTROL 2 -#define DRM_MINOR_RENDER 3 +enum drm_minor_type { + DRM_MINOR_LEGACY, + DRM_MINOR_CONTROL, + DRM_MINOR_RENDER, + DRM_MINOR_CNT, +}; /** * Info file list entry. This structure represents a debugfs or proc file to -- cgit v1.2.3 From 099d1c290e2ebc3b798961a6c177c3aef5f0b789 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 10:21:36 +0100 Subject: drm: provide device-refcount Lets not trick ourselves into thinking "drm_device" objects are not ref-counted. That's just utterly stupid. We manage "drm_minor" objects on each drm-device and each minor can have an unlimited number of open handles. Each of these handles has the drm_minor (and thus the drm_device) as private-data in the file-handle. Therefore, we may not destroy "drm_device" until all these handles are closed. It is *not* possible to reset all these pointers atomically and restrict access to them, and this is *not* how this is done! Instead, we use ref-counts to make sure the object is valid and not freed. Note that we currently use "dev->open_count" for that, which is *exactly* the same as a reference-count, just open coded. So this patch doesn't change any semantics on DRM devices (well, this patch just introduces the ref-count, anyway. Follow-up patches will replace open_count by it). Also note that generic VFS revoke support could allow us to drop this ref-count again. We could then just synchronously disable any fops->xy() calls. However, this is not the case, yet, and no such patches are in sight (and I seriously question the idea of dropping the ref-cnt again). Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_pci.c | 2 +- drivers/gpu/drm/drm_platform.c | 2 +- drivers/gpu/drm/drm_stub.c | 56 +++++++++++++++++++++++++++++++----------- drivers/gpu/drm/drm_usb.c | 2 +- drivers/gpu/drm/tegra/bus.c | 2 +- include/drm/drmP.h | 5 +++- 6 files changed, 50 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 5736aaa7e86c..9ded847b05b4 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -351,7 +351,7 @@ err_agp: drm_pci_agp_destroy(dev); pci_disable_device(pdev); err_free: - drm_dev_free(dev); + drm_dev_unref(dev); return ret; } EXPORT_SYMBOL(drm_get_pci_dev); diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index 21fc82006b78..319ff5385601 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -64,7 +64,7 @@ static int drm_get_platform_dev(struct platform_device *platdev, return 0; err_free: - drm_dev_free(dev); + drm_dev_unref(dev); return ret; } diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 98a33c580ca1..f2f0249304b7 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -392,7 +392,7 @@ void drm_put_dev(struct drm_device *dev) } drm_dev_unregister(dev); - drm_dev_free(dev); + drm_dev_unref(dev); } EXPORT_SYMBOL(drm_put_dev); @@ -425,6 +425,9 @@ EXPORT_SYMBOL(drm_unplug_dev); * Call drm_dev_register() to advertice the device to user space and register it * with other core subsystems. * + * The initial ref-count of the object is 1. Use drm_dev_ref() and + * drm_dev_unref() to take and drop further ref-counts. + * * RETURNS: * Pointer to new DRM device, or NULL if out of memory. */ @@ -438,6 +441,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, if (!dev) return NULL; + kref_init(&dev->ref); dev->dev = parent; dev->driver = driver; @@ -481,18 +485,10 @@ err_free: } EXPORT_SYMBOL(drm_dev_alloc); -/** - * drm_dev_free - Free DRM device - * @dev: DRM device to free - * - * Free a DRM device that has previously been allocated via drm_dev_alloc(). - * You must not use kfree() instead or you will leak memory. - * - * This must not be called once the device got registered. Use drm_put_dev() - * instead, which then calls drm_dev_free(). - */ -void drm_dev_free(struct drm_device *dev) +static void drm_dev_release(struct kref *ref) { + struct drm_device *dev = container_of(ref, struct drm_device, ref); + drm_put_minor(dev->control); drm_put_minor(dev->render); drm_put_minor(dev->primary); @@ -506,7 +502,39 @@ void drm_dev_free(struct drm_device *dev) kfree(dev->devname); kfree(dev); } -EXPORT_SYMBOL(drm_dev_free); + +/** + * drm_dev_ref - Take reference of a DRM device + * @dev: device to take reference of or NULL + * + * This increases the ref-count of @dev by one. You *must* already own a + * reference when calling this. Use drm_dev_unref() to drop this reference + * again. + * + * This function never fails. However, this function does not provide *any* + * guarantee whether the device is alive or running. It only provides a + * reference to the object and the memory associated with it. + */ +void drm_dev_ref(struct drm_device *dev) +{ + if (dev) + kref_get(&dev->ref); +} +EXPORT_SYMBOL(drm_dev_ref); + +/** + * drm_dev_unref - Drop reference of a DRM device + * @dev: device to drop reference of or NULL + * + * This decreases the ref-count of @dev by one. The device is destroyed if the + * ref-count drops to zero. + */ +void drm_dev_unref(struct drm_device *dev) +{ + if (dev) + kref_put(&dev->ref, drm_dev_release); +} +EXPORT_SYMBOL(drm_dev_unref); /** * drm_dev_register - Register DRM device @@ -581,7 +609,7 @@ EXPORT_SYMBOL(drm_dev_register); * * Unregister the DRM device from the system. This does the reverse of * drm_dev_register() but does not deallocate the device. The caller must call - * drm_dev_free() to free all resources. + * drm_dev_unref() to drop their final reference. */ void drm_dev_unregister(struct drm_device *dev) { diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index 0f8cb1ae7607..c3406aad2944 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -30,7 +30,7 @@ int drm_get_usb_dev(struct usb_interface *interface, return 0; err_free: - drm_dev_free(dev); + drm_dev_unref(dev); return ret; } diff --git a/drivers/gpu/drm/tegra/bus.c b/drivers/gpu/drm/tegra/bus.c index e38e5967d77b..71cef5c13dc8 100644 --- a/drivers/gpu/drm/tegra/bus.c +++ b/drivers/gpu/drm/tegra/bus.c @@ -63,7 +63,7 @@ int drm_host1x_init(struct drm_driver *driver, struct host1x_device *device) return 0; err_free: - drm_dev_free(drm); + drm_dev_unref(drm); return ret; } diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 2c322564ca7d..4e53f1607355 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -43,6 +43,7 @@ #include #endif /* __alpha__ */ #include +#include #include #include #include @@ -1102,6 +1103,7 @@ struct drm_device { /** \name Lifetime Management */ /*@{ */ + struct kref ref; /**< Object ref-count */ struct device *dev; /**< Device structure of bus-device */ struct drm_driver *driver; /**< DRM driver managing the device */ void *dev_private; /**< DRM driver private data */ @@ -1666,7 +1668,8 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map) struct drm_device *drm_dev_alloc(struct drm_driver *driver, struct device *parent); -void drm_dev_free(struct drm_device *dev); +void drm_dev_ref(struct drm_device *dev); +void drm_dev_unref(struct drm_device *dev); int drm_dev_register(struct drm_device *dev, unsigned long flags); void drm_dev_unregister(struct drm_device *dev); /*@}*/ -- cgit v1.2.3 From 1616c525b98deb34b8f4b02eccf0ae3a1310fa27 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 10:49:19 +0100 Subject: drm: add minor-lookup/release helpers Instead of accessing drm_minors_idr directly, this adds a small helper to hide the internals. This will help us later to remove the drm_global_mutex requirement for minor-lookup. Furthermore, this also makes sure that minor->dev is always valid and takes a reference-count to the device as long as the minor is used in an open-file. This way, "struct file*"->private_data->dev is guaranteed to be valid (which it has to, as we cannot reset it). Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_fops.c | 50 +++++++++++++++++++++++++--------------------- drivers/gpu/drm/drm_stub.c | 39 ++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 4 ++++ 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 6466cb5d8b1f..79478191404a 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -79,23 +79,22 @@ static int drm_setup(struct drm_device * dev) */ int drm_open(struct inode *inode, struct file *filp) { - struct drm_device *dev = NULL; - int minor_id = iminor(inode); + struct drm_device *dev; struct drm_minor *minor; - int retcode = 0; + int retcode; int need_setup = 0; struct address_space *old_mapping; struct address_space *old_imapping; - minor = idr_find(&drm_minors_idr, minor_id); - if (!minor) - return -ENODEV; - - if (!(dev = minor->dev)) - return -ENODEV; + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + return PTR_ERR(minor); - if (drm_device_is_unplugged(dev)) - return -ENODEV; + dev = minor->dev; + if (drm_device_is_unplugged(dev)) { + retcode = -ENODEV; + goto err_release; + } if (!dev->open_count++) need_setup = 1; @@ -128,6 +127,8 @@ err_undo: dev->dev_mapping = old_mapping; mutex_unlock(&dev->struct_mutex); dev->open_count--; +err_release: + drm_minor_release(minor); return retcode; } EXPORT_SYMBOL(drm_open); @@ -143,33 +144,33 @@ EXPORT_SYMBOL(drm_open); */ int drm_stub_open(struct inode *inode, struct file *filp) { - struct drm_device *dev = NULL; + struct drm_device *dev; struct drm_minor *minor; - int minor_id = iminor(inode); int err = -ENODEV; const struct file_operations *new_fops; DRM_DEBUG("\n"); mutex_lock(&drm_global_mutex); - minor = idr_find(&drm_minors_idr, minor_id); - if (!minor) - goto out; - - if (!(dev = minor->dev)) - goto out; + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + goto out_unlock; + dev = minor->dev; if (drm_device_is_unplugged(dev)) - goto out; + goto out_release; new_fops = fops_get(dev->driver->fops); if (!new_fops) - goto out; + goto out_release; replace_fops(filp, new_fops); if (filp->f_op->open) err = filp->f_op->open(inode, filp); -out: + +out_release: + drm_minor_release(minor); +out_unlock: mutex_unlock(&drm_global_mutex); return err; } @@ -453,7 +454,8 @@ int drm_lastclose(struct drm_device * dev) int drm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; - struct drm_device *dev = file_priv->minor->dev; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; int retcode = 0; mutex_lock(&drm_global_mutex); @@ -575,6 +577,8 @@ int drm_release(struct inode *inode, struct file *filp) } mutex_unlock(&drm_global_mutex); + drm_minor_release(minor); + return retcode; } EXPORT_SYMBOL(drm_release); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index f2f0249304b7..269048215e82 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -355,6 +355,45 @@ static void drm_unplug_minor(struct drm_minor *minor) idr_remove(&drm_minors_idr, minor->index); } +/** + * drm_minor_acquire - Acquire a DRM minor + * @minor_id: Minor ID of the DRM-minor + * + * Looks up the given minor-ID and returns the respective DRM-minor object. The + * refence-count of the underlying device is increased so you must release this + * object with drm_minor_release(). + * + * As long as you hold this minor, it is guaranteed that the object and the + * minor->dev pointer will stay valid! However, the device may get unplugged and + * unregistered while you hold the minor. + * + * Returns: + * Pointer to minor-object with increased device-refcount, or PTR_ERR on + * failure. + */ +struct drm_minor *drm_minor_acquire(unsigned int minor_id) +{ + struct drm_minor *minor; + + minor = idr_find(&drm_minors_idr, minor_id); + if (!minor) + return ERR_PTR(-ENODEV); + + drm_dev_ref(minor->dev); + return minor; +} + +/** + * drm_minor_release - Release DRM minor + * @minor: Pointer to DRM minor object + * + * Release a minor that was previously acquired via drm_minor_acquire(). + */ +void drm_minor_release(struct drm_minor *minor) +{ + drm_dev_unref(minor->dev); +} + /** * drm_put_minor - Destroy DRM minor * @minor: Minor to destroy diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 4e53f1607355..82963167f161 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1672,6 +1672,10 @@ void drm_dev_ref(struct drm_device *dev); void drm_dev_unref(struct drm_device *dev); int drm_dev_register(struct drm_device *dev, unsigned long flags); void drm_dev_unregister(struct drm_device *dev); + +struct drm_minor *drm_minor_acquire(unsigned int minor_id); +void drm_minor_release(struct drm_minor *minor); + /*@}*/ /* PCI section */ -- cgit v1.2.3 From 05b701f6f60201c9906167351cce50db2e9db7ae Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 12:43:56 +0100 Subject: drm: allocate minors early Instead of waiting for device-registration, we now allocate minor-objects during device allocation. The minors are not registered or assigned an ID. This is still postponed to device-registration. While at it, remove the superfluous output-parameter in drm_get_minor(). The reason for this early allocation is to make dev->primary/control/render available atomically. So once the device is alive, all of them are already set and we never have the situation where one of them is set after another (they're either NULL or set, but never changed). This will eventually allow us to reduce minor-ID allocation to one base-ID instead of a single ID for each. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_stub.c | 110 +++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 39 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 269048215e82..b595b64291a2 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -260,21 +260,49 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data, return 0; } +static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, + unsigned int type) +{ + switch (type) { + case DRM_MINOR_LEGACY: + return &dev->primary; + case DRM_MINOR_RENDER: + return &dev->render; + case DRM_MINOR_CONTROL: + return &dev->control; + default: + return NULL; + } +} + +static int drm_minor_alloc(struct drm_device *dev, unsigned int type) +{ + struct drm_minor *minor; + + minor = kzalloc(sizeof(*minor), GFP_KERNEL); + if (!minor) + return -ENOMEM; + + minor->type = type; + minor->dev = dev; + INIT_LIST_HEAD(&minor->master_list); + + *drm_minor_get_slot(dev, type) = minor; + return 0; +} + /** - * drm_get_minor - Allocate and register new DRM minor + * drm_get_minor - Register DRM minor * @dev: DRM device - * @minor: Pointer to where new minor is stored * @type: Type of minor * - * Allocate a new minor of the given type and register it. A pointer to the new - * minor is returned in @minor. + * Register minor of given type. * Caller must hold the global DRM mutex. * * RETURNS: * 0 on success, negative error code on failure. */ -static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, - int type) +static int drm_get_minor(struct drm_device *dev, unsigned int type) { struct drm_minor *new_minor; int ret; @@ -282,21 +310,16 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, DRM_DEBUG("\n"); + new_minor = *drm_minor_get_slot(dev, type); + if (!new_minor) + 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; - INIT_LIST_HEAD(&new_minor->master_list); idr_replace(&drm_minors_idr, new_minor, minor_id); @@ -314,7 +337,6 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, "DRM: Error sysfs_device_add.\n"); goto err_debugfs; } - *minor = new_minor; DRM_DEBUG("new minor assigned %d\n", minor_id); return 0; @@ -325,10 +347,7 @@ err_debugfs: drm_debugfs_cleanup(new_minor); err_mem: #endif - kfree(new_minor); -err_idr: idr_remove(&drm_minors_idr, minor_id); - *minor = NULL; return ret; } @@ -495,8 +514,24 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, mutex_init(&dev->struct_mutex); mutex_init(&dev->ctxlist_mutex); + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = drm_minor_alloc(dev, DRM_MINOR_CONTROL); + if (ret) + goto err_minors; + } + + if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) { + ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); + if (ret) + goto err_minors; + } + + ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY); + if (ret) + goto err_minors; + if (drm_ht_create(&dev->map_hash, 12)) - goto err_free; + goto err_minors; ret = drm_ctxbitmap_init(dev); if (ret) { @@ -518,7 +553,10 @@ err_ctxbitmap: drm_ctxbitmap_cleanup(dev); err_ht: drm_ht_remove(&dev->map_hash); -err_free: +err_minors: + drm_put_minor(dev->control); + drm_put_minor(dev->render); + drm_put_minor(dev->primary); kfree(dev); return NULL; } @@ -594,26 +632,22 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) mutex_lock(&drm_global_mutex); - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); - if (ret) - goto out_unlock; - } + ret = drm_get_minor(dev, DRM_MINOR_CONTROL); + if (ret) + goto err_minors; - if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) { - ret = drm_get_minor(dev, &dev->render, DRM_MINOR_RENDER); - if (ret) - goto err_control_node; - } + ret = drm_get_minor(dev, DRM_MINOR_RENDER); + if (ret) + goto err_minors; - ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY); + ret = drm_get_minor(dev, DRM_MINOR_LEGACY); if (ret) - goto err_render_node; + goto err_minors; if (dev->driver->load) { ret = dev->driver->load(dev, flags); if (ret) - goto err_primary_node; + goto err_minors; } /* setup grouping for legacy outputs */ @@ -630,12 +664,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) err_unload: if (dev->driver->unload) dev->driver->unload(dev); -err_primary_node: - drm_unplug_minor(dev->primary); -err_render_node: - drm_unplug_minor(dev->render); -err_control_node: +err_minors: drm_unplug_minor(dev->control); + drm_unplug_minor(dev->render); + drm_unplug_minor(dev->primary); out_unlock: mutex_unlock(&drm_global_mutex); return ret; -- cgit v1.2.3 From bd9dfa98187f6cb671e60d9df0801378e8a99ad9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 12:55:48 +0100 Subject: drm: move drm_put_minor() to drm_minor_free() _put/get() are used for ref-counting, which we clearly don't do here. Rename it to _free() and also use the common drm_minor_* prefix. Furthermore, avoid passing the minor directly but instead use the type like the other functions do, this allows us to reset the slot. We also drop the redundant call to drm_unplug_minor() as drm_minor_free() is only used from paths were that has already be called. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_stub.c | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index b595b64291a2..e46c0904a706 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -291,6 +291,17 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type) return 0; } +static void drm_minor_free(struct drm_device *dev, unsigned int type) +{ + struct drm_minor **slot; + + slot = drm_minor_get_slot(dev, type); + if (*slot) { + kfree(*slot); + *slot = NULL; + } +} + /** * drm_get_minor - Register DRM minor * @dev: DRM device @@ -413,26 +424,6 @@ void drm_minor_release(struct drm_minor *minor) drm_dev_unref(minor->dev); } -/** - * drm_put_minor - Destroy DRM minor - * @minor: Minor to destroy - * - * This calls drm_unplug_minor() on the given minor and then frees it. Nothing - * is done if @minor is NULL. It is fine to call this on already unplugged - * minors. - * The global DRM mutex must be held by the caller. - */ -static void drm_put_minor(struct drm_minor *minor) -{ - if (!minor) - return; - - DRM_DEBUG("release secondary minor %d\n", minor->index); - - drm_unplug_minor(minor); - kfree(minor); -} - /** * Called via drm_exit() at module unload time or when pci device is * unplugged. @@ -554,9 +545,9 @@ err_ctxbitmap: err_ht: drm_ht_remove(&dev->map_hash); err_minors: - drm_put_minor(dev->control); - drm_put_minor(dev->render); - drm_put_minor(dev->primary); + drm_minor_free(dev, DRM_MINOR_LEGACY); + drm_minor_free(dev, DRM_MINOR_RENDER); + drm_minor_free(dev, DRM_MINOR_CONTROL); kfree(dev); return NULL; } @@ -566,16 +557,16 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref); - drm_put_minor(dev->control); - drm_put_minor(dev->render); - drm_put_minor(dev->primary); - if (dev->driver->driver_features & DRIVER_GEM) drm_gem_destroy(dev); drm_ctxbitmap_cleanup(dev); drm_ht_remove(&dev->map_hash); + drm_minor_free(dev, DRM_MINOR_LEGACY); + drm_minor_free(dev, DRM_MINOR_RENDER); + drm_minor_free(dev, DRM_MINOR_CONTROL); + kfree(dev->devname); kfree(dev); } -- cgit v1.2.3 From afcdbc867460b7ee4119bf4904e60f0e171c6dfb Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 12:57:05 +0100 Subject: drm: rename drm_unplug/get_minor() to drm_minor_register/unregister() drm_get_minor() no longer allocates objects, and drm_unplug_minor() is now the exact reverse of it. Rename it to _register/unregister() so their name actually says what they do. Furthermore, remove the direct minor-ptr and instead pass the minor-type. This way we know the actual slot of the minor and can reset it if required. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_stub.c | 54 +++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index e46c0904a706..488600013e35 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -302,18 +302,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type) } } -/** - * drm_get_minor - Register DRM minor - * @dev: DRM device - * @type: Type of minor - * - * Register minor of given type. - * Caller must hold the global DRM mutex. - * - * RETURNS: - * 0 on success, negative error code on failure. - */ -static int drm_get_minor(struct drm_device *dev, unsigned int type) +static int drm_minor_register(struct drm_device *dev, unsigned int type) { struct drm_minor *new_minor; int ret; @@ -362,18 +351,11 @@ err_mem: return ret; } -/** - * drm_unplug_minor - Unplug DRM minor - * @minor: Minor to unplug - * - * Unplugs the given DRM minor but keeps the object. So after this returns, - * minor->dev is still valid so existing open-files can still access it to get - * device information from their drm_file ojects. - * If the minor is already unplugged or if @minor is NULL, nothing is done. - * The global DRM mutex must be held by the caller. - */ -static void drm_unplug_minor(struct drm_minor *minor) +static void drm_minor_unregister(struct drm_device *dev, unsigned int type) { + struct drm_minor *minor; + + minor = *drm_minor_get_slot(dev, type); if (!minor || !minor->kdev) return; @@ -448,11 +430,9 @@ EXPORT_SYMBOL(drm_put_dev); void drm_unplug_dev(struct drm_device *dev) { /* for a USB device */ - if (drm_core_check_feature(dev, DRIVER_MODESET)) - drm_unplug_minor(dev->control); - if (dev->render) - drm_unplug_minor(dev->render); - drm_unplug_minor(dev->primary); + drm_minor_unregister(dev, DRM_MINOR_LEGACY); + drm_minor_unregister(dev, DRM_MINOR_RENDER); + drm_minor_unregister(dev, DRM_MINOR_CONTROL); mutex_lock(&drm_global_mutex); @@ -623,15 +603,15 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) mutex_lock(&drm_global_mutex); - ret = drm_get_minor(dev, DRM_MINOR_CONTROL); + ret = drm_minor_register(dev, DRM_MINOR_CONTROL); if (ret) goto err_minors; - ret = drm_get_minor(dev, DRM_MINOR_RENDER); + ret = drm_minor_register(dev, DRM_MINOR_RENDER); if (ret) goto err_minors; - ret = drm_get_minor(dev, DRM_MINOR_LEGACY); + ret = drm_minor_register(dev, DRM_MINOR_LEGACY); if (ret) goto err_minors; @@ -656,9 +636,9 @@ err_unload: if (dev->driver->unload) dev->driver->unload(dev); err_minors: - drm_unplug_minor(dev->control); - drm_unplug_minor(dev->render); - drm_unplug_minor(dev->primary); + drm_minor_unregister(dev, DRM_MINOR_LEGACY); + drm_minor_unregister(dev, DRM_MINOR_RENDER); + drm_minor_unregister(dev, DRM_MINOR_CONTROL); out_unlock: mutex_unlock(&drm_global_mutex); return ret; @@ -690,8 +670,8 @@ void drm_dev_unregister(struct drm_device *dev) list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_rmmap(dev, r_list->map); - drm_unplug_minor(dev->control); - drm_unplug_minor(dev->render); - drm_unplug_minor(dev->primary); + drm_minor_unregister(dev, DRM_MINOR_LEGACY); + drm_minor_unregister(dev, DRM_MINOR_RENDER); + drm_minor_unregister(dev, DRM_MINOR_CONTROL); } EXPORT_SYMBOL(drm_dev_unregister); -- cgit v1.2.3 From cb0f93238b89c6178842ba89ecc1cd311f1a3e75 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 13:01:08 +0100 Subject: drm: remove unneeded #ifdef CONFIG_DEBUGFS No need to check for DEBUGFS, we already have dummy-fallbacks in our headers. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_stub.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 488600013e35..fe9595b750ea 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -323,13 +323,11 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) idr_replace(&drm_minors_idr, new_minor, minor_id); -#if defined(CONFIG_DEBUG_FS) ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); goto err_mem; } -#endif ret = drm_sysfs_device_add(new_minor); if (ret) { @@ -343,10 +341,8 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) err_debugfs: -#if defined(CONFIG_DEBUG_FS) drm_debugfs_cleanup(new_minor); err_mem: -#endif idr_remove(&drm_minors_idr, minor_id); return ret; } @@ -359,10 +355,7 @@ static void drm_minor_unregister(struct drm_device *dev, unsigned int type) if (!minor || !minor->kdev) return; -#if defined(CONFIG_DEBUG_FS) drm_debugfs_cleanup(minor); -#endif - drm_sysfs_device_remove(minor); idr_remove(&drm_minors_idr, minor->index); } -- cgit v1.2.3 From 5817878c6f4221c3ace4af63260080635063371e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 29 Jan 2014 13:12:31 +0100 Subject: drm: remove redundant minor->device field Whenever we access minor->device, we are in a minor->kdev->...->fops callback so the minor->kdev pointer *must* be valid. Thus, simply use minor->kdev->devt instead of minor->device and remove the redundant field. Signed-off-by: David Herrmann Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_drv.c | 4 ++-- drivers/gpu/drm/drm_fops.c | 2 +- drivers/gpu/drm/drm_stub.c | 1 - include/drm/drmP.h | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 345be03c23db..ec651be2f3cb 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -344,7 +344,7 @@ long drm_ioctl(struct file *filp, DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, ioctl->name); /* Do not trust userspace, use our own definition */ @@ -402,7 +402,7 @@ long drm_ioctl(struct file *filp, if (!ioctl) DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, cmd, nr); if (kdata != stack_kdata) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 79478191404a..4ce5318d14bc 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -471,7 +471,7 @@ int drm_release(struct inode *inode, struct file *filp) DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); /* Release any auth tokens that might point to this file_priv, diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index fe9595b750ea..96fe5dec3822 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -318,7 +318,6 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) if (minor_id < 0) return minor_id; - new_minor->device = MKDEV(DRM_MAJOR, minor_id); new_minor->index = minor_id; idr_replace(&drm_minors_idr, new_minor, minor_id); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 82963167f161..538079030be0 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1043,7 +1043,6 @@ struct drm_info_node { struct drm_minor { int index; /**< Minor device number */ int type; /**< Control or render */ - dev_t device; /**< Device number for mknod */ struct device *kdev; /**< Linux device */ struct drm_device *dev; -- cgit v1.2.3 From 1abbc43761793938fba9ae745e01d5b4730a9914 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 24 Feb 2014 15:32:00 +0100 Subject: drm: coding-style fixes in minor handling Properly name goto-labels, remove empty lines and use DRM_ERROR if possible. Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_stub.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 96fe5dec3822..5268ffc5d94e 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -325,23 +325,21 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); - goto err_mem; + goto err_id; } ret = drm_sysfs_device_add(new_minor); if (ret) { - printk(KERN_ERR - "DRM: Error sysfs_device_add.\n"); + DRM_ERROR("DRM: Error sysfs_device_add.\n"); goto err_debugfs; } DRM_DEBUG("new minor assigned %d\n", minor_id); return 0; - err_debugfs: drm_debugfs_cleanup(new_minor); -err_mem: +err_id: idr_remove(&drm_minors_idr, minor_id); return ret; } -- cgit v1.2.3 From 7d86cf1a4fc0c0bdb6947185c6fe71301dfea7b1 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 24 Feb 2014 15:35:09 +0100 Subject: drm: inline drm_minor_get_id() We can significantly simplify this helper by using plain multiplication. Note that we converted the minor-type to an enum earlier so this didn't work before. We also fix a minor range-bug here: the limit argument of idr_alloc() is *exclusive*, not inclusive, so we should use 64 instead of 63 as offset. Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_stub.c | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 5268ffc5d94e..83ef4a63358c 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -117,26 +117,6 @@ void drm_ut_debug_printk(unsigned int request_level, } EXPORT_SYMBOL(drm_ut_debug_printk); -static int drm_minor_get_id(struct drm_device *dev, int type) -{ - int ret; - int base = 0, limit = 63; - - if (type == DRM_MINOR_CONTROL) { - base += 64; - limit = base + 63; - } else if (type == DRM_MINOR_RENDER) { - base += 128; - limit = base + 63; - } - - mutex_lock(&dev->struct_mutex); - ret = idr_alloc(&drm_minors_idr, NULL, base, limit, GFP_KERNEL); - mutex_unlock(&dev->struct_mutex); - - return ret == -ENOSPC ? -EINVAL : ret; -} - struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; @@ -314,7 +294,12 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) if (!new_minor) return 0; - minor_id = drm_minor_get_id(dev, type); + minor_id = idr_alloc(&drm_minors_idr, + NULL, + 64 * type, + 64 * (type + 1), + GFP_KERNEL); + if (minor_id < 0) return minor_id; -- cgit v1.2.3 From 0d639883ee26359e1bf38195df1dbca0f879e239 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 24 Feb 2014 15:53:25 +0100 Subject: drm: make minors independent of global lock We used to protect minor-lookup and setup by the global drm lock. To continue our attempts of dropping drm_global_mutex, this patch makes the minor management independent of it. Furthermore, we make it all atomic and switch to spin-locks instead of a mutex. Now that minor-lookup is independent, we also move the "drm_is_unplugged()" test into the minor-lookup path. There is no reason to ever return a minor for unplugged objects, so keep that logic internal. Signed-off-by: David Herrmann --- drivers/gpu/drm/drm_fops.c | 11 +--------- drivers/gpu/drm/drm_stub.c | 51 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 4ce5318d14bc..8f46fe273ba3 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -39,7 +39,7 @@ #include #include -/* from BKL pushdown: note that nothing else serializes idr_find() */ +/* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); EXPORT_SYMBOL(drm_global_mutex); @@ -91,11 +91,6 @@ int drm_open(struct inode *inode, struct file *filp) return PTR_ERR(minor); dev = minor->dev; - if (drm_device_is_unplugged(dev)) { - retcode = -ENODEV; - goto err_release; - } - if (!dev->open_count++) need_setup = 1; mutex_lock(&dev->struct_mutex); @@ -127,7 +122,6 @@ err_undo: dev->dev_mapping = old_mapping; mutex_unlock(&dev->struct_mutex); dev->open_count--; -err_release: drm_minor_release(minor); return retcode; } @@ -157,9 +151,6 @@ int drm_stub_open(struct inode *inode, struct file *filp) goto out_unlock; dev = minor->dev; - if (drm_device_is_unplugged(dev)) - goto out_release; - new_fops = fops_get(dev->driver->fops); if (!new_fops) goto out_release; diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 83ef4a63358c..c23eaf6442ff 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -70,6 +70,7 @@ module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); +static DEFINE_SPINLOCK(drm_minor_lock); struct idr drm_minors_idr; struct class *drm_class; @@ -240,6 +241,19 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data, return 0; } +/* + * DRM Minors + * A DRM device can provide several char-dev interfaces on the DRM-Major. Each + * of them is represented by a drm_minor object. Depending on the capabilities + * of the device-driver, different interfaces are registered. + * + * Minors can be accessed via dev->$minor_name. This pointer is either + * NULL or a valid drm_minor pointer and stays valid as long as the device is + * valid. This means, DRM minors have the same life-time as the underlying + * device. However, this doesn't mean that the minor is active. Minors are + * registered and unregistered dynamically according to device-state. + */ + static struct drm_minor **drm_minor_get_slot(struct drm_device *dev, unsigned int type) { @@ -285,6 +299,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type) static int drm_minor_register(struct drm_device *dev, unsigned int type) { struct drm_minor *new_minor; + unsigned long flags; int ret; int minor_id; @@ -294,19 +309,21 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) if (!new_minor) return 0; + idr_preload(GFP_KERNEL); + spin_lock_irqsave(&drm_minor_lock, flags); minor_id = idr_alloc(&drm_minors_idr, NULL, 64 * type, 64 * (type + 1), - GFP_KERNEL); + GFP_NOWAIT); + spin_unlock_irqrestore(&drm_minor_lock, flags); + idr_preload_end(); if (minor_id < 0) return minor_id; new_minor->index = minor_id; - idr_replace(&drm_minors_idr, new_minor, minor_id); - ret = drm_debugfs_init(new_minor, minor_id, drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); @@ -319,27 +336,40 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) goto err_debugfs; } + /* replace NULL with @minor so lookups will succeed from now on */ + spin_lock_irqsave(&drm_minor_lock, flags); + idr_replace(&drm_minors_idr, new_minor, new_minor->index); + spin_unlock_irqrestore(&drm_minor_lock, flags); + DRM_DEBUG("new minor assigned %d\n", minor_id); return 0; err_debugfs: drm_debugfs_cleanup(new_minor); err_id: + spin_lock_irqsave(&drm_minor_lock, flags); idr_remove(&drm_minors_idr, minor_id); + spin_unlock_irqrestore(&drm_minor_lock, flags); + new_minor->index = 0; return ret; } static void drm_minor_unregister(struct drm_device *dev, unsigned int type) { struct drm_minor *minor; + unsigned long flags; minor = *drm_minor_get_slot(dev, type); if (!minor || !minor->kdev) return; + spin_lock_irqsave(&drm_minor_lock, flags); + idr_remove(&drm_minors_idr, minor->index); + spin_unlock_irqrestore(&drm_minor_lock, flags); + minor->index = 0; + drm_debugfs_cleanup(minor); drm_sysfs_device_remove(minor); - idr_remove(&drm_minors_idr, minor->index); } /** @@ -361,12 +391,21 @@ static void drm_minor_unregister(struct drm_device *dev, unsigned int type) struct drm_minor *drm_minor_acquire(unsigned int minor_id) { struct drm_minor *minor; + unsigned long flags; + spin_lock_irqsave(&drm_minor_lock, flags); minor = idr_find(&drm_minors_idr, minor_id); - if (!minor) + if (minor) + drm_dev_ref(minor->dev); + spin_unlock_irqrestore(&drm_minor_lock, flags); + + if (!minor) { + return ERR_PTR(-ENODEV); + } else if (drm_device_is_unplugged(minor->dev)) { + drm_dev_unref(minor->dev); return ERR_PTR(-ENODEV); + } - drm_dev_ref(minor->dev); return minor; } -- cgit v1.2.3