diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fops.c')
| -rw-r--r-- | drivers/gpu/drm/drm_fops.c | 228 | 
1 files changed, 124 insertions, 104 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 3f84277d703..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, @@ -454,21 +476,22 @@ int drm_release(struct inode *inode, struct file *filp)  				list_del(&pos->head);  				kfree(pos); -				--dev->ctx_count;  			}  		}  	}  	mutex_unlock(&dev->ctxlist_mutex); -	mutex_lock(&dev->struct_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;  		}  		/** @@ -483,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 */ @@ -492,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); @@ -516,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);  | 
