diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fops.c')
| -rw-r--r-- | drivers/gpu/drm/drm_fops.c | 247 |
1 files changed, 143 insertions, 104 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 4be8e09a32e..021fe5d11df 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -39,12 +39,11 @@ #include <linux/slab.h> #include <linux/module.h> -/* from BKL pushdown: note that nothing else serializes idr_find() */ +/* from BKL pushdown */ 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); +static int drm_open_helper(struct file *filp, struct drm_minor *minor); static int drm_setup(struct drm_device * dev) { @@ -79,41 +78,25 @@ 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; - if (drm_device_is_unplugged(dev)) - return -ENODEV; + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + return PTR_ERR(minor); + dev = minor->dev; if (!dev->open_count++) need_setup = 1; - mutex_lock(&dev->struct_mutex); - old_imapping = inode->i_mapping; - old_mapping = dev->dev_mapping; - if (old_mapping == NULL) - dev->dev_mapping = &inode->i_data; - /* ihold ensures nobody can remove inode with our i_data */ - ihold(container_of(dev->dev_mapping, struct inode, i_data)); - inode->i_mapping = dev->dev_mapping; - filp->f_mapping = dev->dev_mapping; - mutex_unlock(&dev->struct_mutex); - retcode = drm_open_helper(inode, filp, dev); + /* share address_space across all char-devs of a single device */ + filp->f_mapping = dev->anon_inode->i_mapping; + + retcode = drm_open_helper(filp, minor); if (retcode) goto err_undo; - atomic_inc(&dev->counts[_DRM_STAT_OPENS]); if (need_setup) { retcode = drm_setup(dev); if (retcode) @@ -122,13 +105,8 @@ int drm_open(struct inode *inode, struct file *filp) return 0; err_undo: - mutex_lock(&dev->struct_mutex); - filp->f_mapping = old_imapping; - inode->i_mapping = old_imapping; - iput(container_of(dev->dev_mapping, struct inode, i_data)); - dev->dev_mapping = old_mapping; - mutex_unlock(&dev->struct_mutex); dev->open_count--; + drm_minor_release(minor); return retcode; } EXPORT_SYMBOL(drm_open); @@ -144,38 +122,30 @@ 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 *old_fops; + 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; - - if (drm_device_is_unplugged(dev)) - goto out; - - old_fops = filp->f_op; - filp->f_op = fops_get(dev->driver->fops); - if (filp->f_op == NULL) { - filp->f_op = old_fops; - goto out; - } - if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) { - fops_put(filp->f_op); - filp->f_op = fops_get(old_fops); - } - fops_put(old_fops); - -out: + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + goto out_unlock; + + dev = minor->dev; + new_fops = fops_get(dev->driver->fops); + if (!new_fops) + goto out_release; + + replace_fops(filp, new_fops); + if (filp->f_op->open) + err = filp->f_op->open(inode, filp); + +out_release: + drm_minor_release(minor); +out_unlock: mutex_unlock(&drm_global_mutex); return err; } @@ -200,18 +170,16 @@ static int drm_cpu_valid(void) /** * Called whenever a process opens /dev/drm. * - * \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) +static int drm_open_helper(struct file *filp, struct drm_minor *minor) { - int minor_id = iminor(inode); + struct drm_device *dev = minor->dev; struct drm_file *priv; int ret; @@ -222,7 +190,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) @@ -232,15 +200,11 @@ 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; - priv->ioctl_count = 0; /* for compatibility root is always authenticated */ - priv->authenticated = capable(CAP_SYS_ADMIN); + priv->always_authenticated = capable(CAP_SYS_ADMIN); + priv->authenticated = priv->always_authenticated; priv->lock_count = 0; INIT_LIST_HEAD(&priv->lhead); @@ -264,12 +228,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp, /* if there is no current master make this fd it, but do not create * any master object for render clients */ - mutex_lock(&dev->struct_mutex); - if (!priv->minor->master && !drm_is_render_client(priv)) { + mutex_lock(&dev->master_mutex); + if (drm_is_primary_client(priv) && !priv->minor->master) { /* create a new master */ priv->minor->master = drm_master_create(priv->minor); if (!priv->minor->master) { - mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; goto out_close; } @@ -277,37 +240,31 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->is_master = 1; /* take another reference for the copy in the local file priv */ priv->master = drm_master_get(priv->minor->master); - priv->authenticated = 1; - mutex_unlock(&dev->struct_mutex); if (dev->driver->master_create) { ret = dev->driver->master_create(dev, priv->master); if (ret) { - mutex_lock(&dev->struct_mutex); /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); - mutex_unlock(&dev->struct_mutex); goto out_close; } } - mutex_lock(&dev->struct_mutex); if (dev->driver->master_set) { ret = dev->driver->master_set(dev, priv, true); if (ret) { /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); - mutex_unlock(&dev->struct_mutex); goto out_close; } } - } else if (!drm_is_render_client(priv)) { + } else if (drm_is_primary_client(priv)) { /* get a reference to the master */ priv->master = drm_master_get(priv->minor->master); } - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->master_mutex); mutex_lock(&dev->struct_mutex); list_add(&priv->lhead, &dev->filelist); @@ -325,7 +282,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, pci_dev_put(pci_dev); } if (!dev->hose) { - struct pci_bus *b = pci_bus_b(pci_root_buses.next); + struct pci_bus *b = list_entry(pci_root_buses.next, + struct pci_bus, node); if (b) dev->hose = b->sysdata; } @@ -335,6 +293,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, return 0; out_close: + mutex_unlock(&dev->master_mutex); if (dev->driver->postclose) dev->driver->postclose(dev, priv); out_prime_destroy: @@ -342,7 +301,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; @@ -379,13 +337,76 @@ static void drm_events_release(struct drm_file *file_priv) } /* Remove unconsumed events */ - list_for_each_entry_safe(e, et, &file_priv->event_list, link) + list_for_each_entry_safe(e, et, &file_priv->event_list, link) { + list_del(&e->link); e->destroy(e); + } spin_unlock_irqrestore(&dev->event_lock, flags); } /** + * drm_legacy_dev_reinit + * + * Reinitializes a legacy/ums drm device in it's lastclose function. + */ +static void drm_legacy_dev_reinit(struct drm_device *dev) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + dev->sigdata.lock = NULL; + + dev->context_flag = 0; + dev->last_context = 0; + dev->if_version = 0; +} + +/** + * Take down the DRM device. + * + * \param dev DRM device structure. + * + * Frees every resource in \p dev. + * + * \sa drm_device + */ +int drm_lastclose(struct drm_device * dev) +{ + struct drm_vma_entry *vma, *vma_temp; + + DRM_DEBUG("\n"); + + if (dev->driver->lastclose) + dev->driver->lastclose(dev); + DRM_DEBUG("driver lastclose completed\n"); + + if (dev->irq_enabled && !drm_core_check_feature(dev, DRIVER_MODESET)) + drm_irq_uninstall(dev); + + mutex_lock(&dev->struct_mutex); + + drm_agp_clear(dev); + + drm_legacy_sg_cleanup(dev); + + /* Clear vma list (only built for debugging) */ + list_for_each_entry_safe(vma, vma_temp, &dev->vmalist, head) { + list_del(&vma->head); + kfree(vma); + } + + drm_legacy_dma_takedown(dev); + + mutex_unlock(&dev->struct_mutex); + + drm_legacy_dev_reinit(dev); + + DRM_DEBUG("lastclose completed\n"); + return 0; +} + +/** * Release file. * * \param inode device inode @@ -400,7 +421,8 @@ static void drm_events_release(struct drm_file *file_priv) 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); @@ -416,7 +438,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, @@ -439,17 +461,37 @@ int drm_release(struct inode *inode, struct file *filp) if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); - drm_legacy_ctxbitmap_release(dev, file_priv); + mutex_lock(&dev->ctxlist_mutex); + if (!list_empty(&dev->ctxlist)) { + struct drm_ctx_list *pos, *n; - mutex_lock(&dev->struct_mutex); + list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { + if (pos->tag == file_priv && + pos->handle != DRM_KERNEL_CONTEXT) { + if (dev->driver->context_dtor) + dev->driver->context_dtor(dev, + pos->handle); + + drm_ctxbitmap_free(dev, pos->handle); + + list_del(&pos->head); + kfree(pos); + } + } + } + mutex_unlock(&dev->ctxlist_mutex); + + mutex_lock(&dev->master_mutex); if (file_priv->is_master) { struct drm_master *master = file_priv->master; struct drm_file *temp; + + mutex_lock(&dev->struct_mutex); list_for_each_entry(temp, &dev->filelist, lhead) { if ((temp->master == file_priv->master) && (temp != file_priv)) - temp->authenticated = 0; + temp->authenticated = temp->always_authenticated; } /** @@ -464,6 +506,7 @@ int drm_release(struct inode *inode, struct file *filp) master->lock.file_priv = NULL; wake_up_interruptible_all(&master->lock.lock_queue); } + mutex_unlock(&dev->struct_mutex); if (file_priv->minor->master == file_priv->master) { /* drop the reference held my the minor */ @@ -473,13 +516,13 @@ int drm_release(struct inode *inode, struct file *filp) } } - BUG_ON(dev->dev_mapping == NULL); - iput(container_of(dev->dev_mapping, struct inode, i_data)); - - /* drop the reference held my the file priv */ + /* drop the master reference held by the file priv */ if (file_priv->master) drm_master_put(&file_priv->master); file_priv->is_master = 0; + mutex_unlock(&dev->master_mutex); + + mutex_lock(&dev->struct_mutex); list_del(&file_priv->lhead); mutex_unlock(&dev->struct_mutex); @@ -497,19 +540,15 @@ int drm_release(struct inode *inode, struct file *filp) * End inline drm_release */ - atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); if (!--dev->open_count) { - if (atomic_read(&dev->ioctl_count)) { - DRM_ERROR("Device busy: %d\n", - atomic_read(&dev->ioctl_count)); - retcode = -EBUSY; - } else - retcode = drm_lastclose(dev); + retcode = drm_lastclose(dev); if (drm_device_is_unplugged(dev)) drm_put_dev(dev); } mutex_unlock(&drm_global_mutex); + drm_minor_release(minor); + return retcode; } EXPORT_SYMBOL(drm_release); |
