diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fops.c')
| -rw-r--r-- | drivers/gpu/drm/drm_fops.c | 408 | 
1 files changed, 191 insertions, 217 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index b744dad5c23..021fe5d11df 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -34,77 +34,34 @@   * OTHER DEALINGS IN THE SOFTWARE.   */ -#include "drmP.h" +#include <drm/drmP.h>  #include <linux/poll.h>  #include <linux/slab.h> -#include <linux/smp_lock.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)  { -	int i;  	int ret; -	if (dev->driver->firstopen) { +	if (dev->driver->firstopen && +	    !drm_core_check_feature(dev, DRIVER_MODESET)) {  		ret = dev->driver->firstopen(dev);  		if (ret != 0)  			return ret;  	} -	atomic_set(&dev->ioctl_count, 0); -	atomic_set(&dev->vma_count, 0); - -	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && -	    !drm_core_check_feature(dev, DRIVER_MODESET)) { -		dev->buf_use = 0; -		atomic_set(&dev->buf_alloc, 0); - -		i = drm_dma_setup(dev); -		if (i < 0) -			return i; -	} - -	for (i = 0; i < ARRAY_SIZE(dev->counts); i++) -		atomic_set(&dev->counts[i], 0); - -	dev->sigdata.lock = NULL; - -	dev->queue_count = 0; -	dev->queue_reserved = 0; -	dev->queue_slots = 0; -	dev->queuelist = NULL; -	dev->context_flag = 0; -	dev->interrupt_flag = 0; -	dev->dma_flag = 0; -	dev->last_context = 0; -	dev->last_switch = 0; -	dev->last_checked = 0; -	init_waitqueue_head(&dev->context_wait); -	dev->if_version = 0; - -	dev->ctx_start = 0; -	dev->lck_start = 0; +	ret = drm_legacy_dma_setup(dev); +	if (ret < 0) +		return ret; -	dev->buf_async = NULL; -	init_waitqueue_head(&dev->buf_readers); -	init_waitqueue_head(&dev->buf_writers);  	DRM_DEBUG("\n"); - -	/* -	 * The kernel's context could be created here, but is now created -	 * in drm_dma_enqueue.  This is more resource-efficient for -	 * hardware that does not do DMA, but may mean that -	 * drm_select_queue fails between the time the interrupt is -	 * initialized and the time the queues are initialized. -	 */ -  	return 0;  } @@ -121,35 +78,35 @@ 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; - -	minor = idr_find(&drm_minors_idr, minor_id); -	if (!minor) -		return -ENODEV; - -	if (!(dev = minor->dev)) -		return -ENODEV; - -	retcode = drm_open_helper(inode, filp, dev); -	if (!retcode) { -		atomic_inc(&dev->counts[_DRM_STAT_OPENS]); -		if (!dev->open_count++) -			retcode = drm_setup(dev); -	} -	if (!retcode) { -		mutex_lock(&dev->struct_mutex); -		if (minor->type == DRM_MINOR_LEGACY) { -			if (dev->dev_mapping == NULL) -				dev->dev_mapping = inode->i_mapping; -			else if (dev->dev_mapping != inode->i_mapping) -				retcode = -ENODEV; -		} -		mutex_unlock(&dev->struct_mutex); +	int retcode; +	int need_setup = 0; + +	minor = drm_minor_acquire(iminor(inode)); +	if (IS_ERR(minor)) +		return PTR_ERR(minor); + +	dev = minor->dev; +	if (!dev->open_count++) +		need_setup = 1; + +	/* 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; +	if (need_setup) { +		retcode = drm_setup(dev); +		if (retcode) +			goto err_undo;  	} +	return 0; +err_undo: +	dev->open_count--; +	drm_minor_release(minor);  	return retcode;  }  EXPORT_SYMBOL(drm_open); @@ -165,35 +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; - -	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;  } @@ -218,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; @@ -237,8 +187,10 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  		return -EBUSY;	/* No exclusive opens */  	if (!drm_cpu_valid())  		return -EINVAL; +	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) @@ -247,15 +199,17 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	filp->private_data = priv;  	priv->filp = filp;  	priv->uid = current_euid(); -	priv->pid = task_pid_nr(current); -	priv->minor = idr_find(&drm_minors_idr, minor_id); -	priv->ioctl_count = 0; +	priv->pid = get_pid(task_pid(current)); +	priv->minor = minor; +  	/* 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);  	INIT_LIST_HEAD(&priv->fbs); +	mutex_init(&priv->fbs_lock);  	INIT_LIST_HEAD(&priv->event_list);  	init_waitqueue_head(&priv->event_wait);  	priv->event_space = 4096; /* set aside 4k for event buffer */ @@ -263,59 +217,54 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	if (dev->driver->driver_features & DRIVER_GEM)  		drm_gem_open(dev, priv); +	if (drm_core_check_feature(dev, DRIVER_PRIME)) +		drm_prime_init_file_private(&priv->prime); +  	if (dev->driver->open) {  		ret = dev->driver->open(dev, priv);  		if (ret < 0) -			goto out_free; +			goto out_prime_destroy;  	} - -	/* if there is no current master make this fd it */ -	mutex_lock(&dev->struct_mutex); -	if (!priv->minor->master) { +	/* if there is no current master make this fd it, but do not create +	 * any master object for render clients */ +	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_free; +			goto out_close;  		}  		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_free; +				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_free; +				goto out_close;  			}  		} -		mutex_unlock(&dev->struct_mutex); -	} else { +	} 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); @@ -333,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;  		} @@ -341,90 +291,32 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  #endif  	return 0; -      out_free: + +out_close: +	mutex_unlock(&dev->master_mutex); +	if (dev->driver->postclose) +		dev->driver->postclose(dev, priv); +out_prime_destroy: +	if (drm_core_check_feature(dev, DRIVER_PRIME)) +		drm_prime_destroy_file_private(&priv->prime); +	if (dev->driver->driver_features & DRIVER_GEM) +		drm_gem_release(dev, priv); +	put_pid(priv->pid);  	kfree(priv);  	filp->private_data = NULL;  	return ret;  } -/** No-op. */ -int drm_fasync(int fd, struct file *filp, int on) -{ -	struct drm_file *priv = filp->private_data; -	struct drm_device *dev = priv->minor->dev; - -	DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, -		  (long)old_encode_dev(priv->minor->device)); -	return fasync_helper(fd, filp, on, &dev->buf_async); -} -EXPORT_SYMBOL(drm_fasync); - -/* - * Reclaim locked buffers; note that this may be a bad idea if the current - * context doesn't have the hw lock... - */ -static void drm_reclaim_locked_buffers(struct drm_device *dev, struct file *f) -{ -	struct drm_file *file_priv = f->private_data; - -	if (drm_i_have_hw_lock(dev, file_priv)) { -		dev->driver->reclaim_buffers_locked(dev, file_priv); -	} else { -		unsigned long _end = jiffies + 3 * DRM_HZ; -		int locked = 0; - -		drm_idlelock_take(&file_priv->master->lock); - -		/* -		 * Wait for a while. -		 */ -		do { -			spin_lock_bh(&file_priv->master->lock.spinlock); -			locked = file_priv->master->lock.idle_has_lock; -			spin_unlock_bh(&file_priv->master->lock.spinlock); -			if (locked) -				break; -			schedule(); -		} while (!time_after_eq(jiffies, _end)); - -		if (!locked) { -			DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n" -				  "\tdriver to use reclaim_buffers_idlelocked() instead.\n" -				  "\tI will go on reclaiming the buffers anyway.\n"); -		} - -		dev->driver->reclaim_buffers_locked(dev, file_priv); -		drm_idlelock_release(&file_priv->master->lock); -	} -} -  static void drm_master_release(struct drm_device *dev, struct file *filp)  {  	struct drm_file *file_priv = filp->private_data; -	if (dev->driver->reclaim_buffers_locked && -	    file_priv->master->lock.hw_lock) -		drm_reclaim_locked_buffers(dev, filp); - -	if (dev->driver->reclaim_buffers_idlelocked && -	    file_priv->master->lock.hw_lock) { -		drm_idlelock_take(&file_priv->master->lock); -		dev->driver->reclaim_buffers_idlelocked(dev, file_priv); -		drm_idlelock_release(&file_priv->master->lock); -	} - -  	if (drm_i_have_hw_lock(dev, file_priv)) {  		DRM_DEBUG("File %p released, freeing lock for context %d\n",  			  filp, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));  		drm_lock_free(&file_priv->master->lock,  			      _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock));  	} - -	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && -	    !dev->driver->reclaim_buffers_locked) { -		dev->driver->reclaim_buffers(dev, file_priv); -	}  }  static void drm_events_release(struct drm_file *file_priv) @@ -445,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 @@ -466,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); @@ -482,21 +438,29 @@ 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, +	   (do that under the drm_global_mutex) */ +	if (file_priv->magic) +		(void) drm_remove_magic(file_priv->master, file_priv->magic); +  	/* if the master has gone away we can't do anything with the lock */  	if (file_priv->minor->master)  		drm_master_release(dev, filp); -	drm_events_release(file_priv); +	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) +		drm_core_reclaim_buffers(dev, file_priv); -	if (dev->driver->driver_features & DRIVER_GEM) -		drm_gem_release(dev, file_priv); +	drm_events_release(file_priv);  	if (dev->driver->driver_features & DRIVER_MODESET)  		drm_fb_release(file_priv); +	if (dev->driver->driver_features & DRIVER_GEM) +		drm_gem_release(dev, file_priv); +  	mutex_lock(&dev->ctxlist_mutex);  	if (!list_empty(&dev->ctxlist)) {  		struct drm_ctx_list *pos, *n; @@ -512,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;  		}  		/** @@ -541,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 */ @@ -550,31 +516,39 @@ int drm_release(struct inode *inode, struct file *filp)  		}  	} -	/* drop the reference held my the file priv */ -	drm_master_put(&file_priv->master); +	/* 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);  	if (dev->driver->postclose)  		dev->driver->postclose(dev, file_priv); + + +	if (drm_core_check_feature(dev, DRIVER_PRIME)) +		drm_prime_destroy_file_private(&file_priv->prime); + +	put_pid(file_priv->pid);  	kfree(file_priv);  	/* ========================================================  	 * 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);  | 
