diff options
Diffstat (limited to 'linux-core/drm_fops.c')
| -rw-r--r-- | linux-core/drm_fops.c | 147 | 
1 files changed, 103 insertions, 44 deletions
| diff --git a/linux-core/drm_fops.c b/linux-core/drm_fops.c index 48c77545..b60ced34 100644 --- a/linux-core/drm_fops.c +++ b/linux-core/drm_fops.c @@ -47,6 +47,7 @@ static int drm_setup(drm_device_t * dev)  	int i;  	int ret; +  	if (dev->driver->firstopen) {  		ret = dev->driver->firstopen(dev);  		if (ret != 0) @@ -56,6 +57,7 @@ static int drm_setup(drm_device_t * dev)  	dev->magicfree.next = NULL;  	/* prebuild the SAREA */ +  	i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);  	if (i != 0)  		return i; @@ -156,6 +158,12 @@ int drm_open(struct inode *inode, struct file *filp)  		}  		spin_unlock(&dev->count_lock);  	} +	mutex_lock(&dev->struct_mutex); +	BUG_ON((dev->dev_mapping != NULL) &&  +	       (dev->dev_mapping != inode->i_mapping)); +	if (dev->dev_mapping == NULL) +		dev->dev_mapping = inode->i_mapping; +	mutex_unlock(&dev->struct_mutex);  	return retcode;  } @@ -233,6 +241,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	int minor = iminor(inode);  	drm_file_t *priv;  	int ret; +	int i,j;  	if (filp->f_flags & O_EXCL)  		return -EBUSY;	/* No exclusive opens */ @@ -256,6 +265,22 @@ static int drm_open_helper(struct inode *inode, struct file *filp,  	priv->authenticated = capable(CAP_SYS_ADMIN);  	priv->lock_count = 0; +	INIT_LIST_HEAD(&priv->user_objects); +	INIT_LIST_HEAD(&priv->refd_objects); + +	for (i=0; i<_DRM_NO_REF_TYPES; ++i) { +		ret = drm_ht_create(&priv->refd_object_hash[i], DRM_FILE_HASH_ORDER); +		if (ret) +			break; +	} + +	if (ret) { +		for(j=0; j<i; ++j) { +			drm_ht_remove(&priv->refd_object_hash[j]); +		} +		goto out_free; +	} +  	if (dev->driver->open) {  		ret = dev->driver->open(dev, priv);  		if (ret < 0) @@ -320,6 +345,53 @@ int drm_fasync(int fd, struct file *filp, int on)  }  EXPORT_SYMBOL(drm_fasync); +static void drm_object_release(struct file *filp) { + +        drm_file_t *priv = filp->private_data; +	struct list_head *head; +	drm_user_object_t *user_object; +	drm_ref_object_t *ref_object; +	int i; + +	/* +	 * Free leftover ref objects created by me. Note that we cannot use +	 * list_for_each() here, as the struct_mutex may be temporarily released  +	 * by the remove_() functions, and thus the lists may be altered. +	 * Also, a drm_remove_ref_object() will not remove it +	 * from the list unless its refcount is 1. +	 */ + +	head = &priv->refd_objects;  +	while (head->next != head) { +		ref_object = list_entry(head->next, drm_ref_object_t, list); +		drm_remove_ref_object(priv, ref_object);		 +		head = &priv->refd_objects;  +	} +		 +	/* +	 * Free leftover user objects created by me. +	 */ + +	head = &priv->user_objects;  +	while (head->next != head) { +		user_object = list_entry(head->next, drm_user_object_t, list); +		drm_remove_user_object(priv, user_object);		 +		head = &priv->user_objects;  +	} + + + + +	for(i=0; i<_DRM_NO_REF_TYPES; ++i) { +		drm_ht_remove(&priv->refd_object_hash[i]); +	} +}			 +		 + + + + +  /**   * Release file.   * @@ -354,58 +426,43 @@ int drm_release(struct inode *inode, struct file *filp)  		  current->pid, (long)old_encode_dev(priv->head->device),  		  dev->open_count); -	if (priv->lock_count && dev->lock.hw_lock && -	    _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && -	    dev->lock.filp == filp) { -		DRM_DEBUG("File %p released, freeing lock for context %d\n", -			  filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); - -		if (dev->driver->reclaim_buffers_locked) -			dev->driver->reclaim_buffers_locked(dev, filp); +	if (dev->driver->reclaim_buffers_locked) { +	        unsigned long _end = jiffies + DRM_HZ*3; -		drm_lock_free(dev, &dev->lock.hw_lock->lock, -			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); +		do { +			retcode = drm_kernel_take_hw_lock(filp); +		} while(retcode && !time_after_eq(jiffies,_end)); -		/* FIXME: may require heavy-handed reset of -		   hardware at this point, possibly -		   processed via a callback to the X -		   server. */ -	} else if (dev->driver->reclaim_buffers_locked && priv->lock_count -		   && dev->lock.hw_lock) { -		/* The lock is required to reclaim buffers */ -		DECLARE_WAITQUEUE(entry, current); - -		add_wait_queue(&dev->lock.lock_queue, &entry); -		for (;;) { -			__set_current_state(TASK_INTERRUPTIBLE); -			if (!dev->lock.hw_lock) { -				/* Device has been unregistered */ -				retcode = -EINTR; -				break; -			} -			if (drm_lock_take(&dev->lock.hw_lock->lock, -					  DRM_KERNEL_CONTEXT)) { -				dev->lock.filp = filp; -				dev->lock.lock_time = jiffies; -				atomic_inc(&dev->counts[_DRM_STAT_LOCKS]); -				break;	/* Got lock */ -			} -			/* Contention */ -			schedule(); -			if (signal_pending(current)) { -				retcode = -ERESTARTSYS; -				break; -			} -		} -		__set_current_state(TASK_RUNNING); -		remove_wait_queue(&dev->lock.lock_queue, &entry);  		if (!retcode) {  			dev->driver->reclaim_buffers_locked(dev, filp); +  			drm_lock_free(dev, &dev->lock.hw_lock->lock, -				      DRM_KERNEL_CONTEXT); +				      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); +		} else { + +			/* +			 * FIXME: This is not a good solution. We should perhaps associate the +			 * DRM lock with a process context, and check whether the current process +			 * holds the lock. Then we can run reclaim buffers locked anyway. +			 */ + +			DRM_ERROR("Reclaim buffers locked deadlock.\n"); +			DRM_ERROR("This is probably a single thread having multiple\n"); +			DRM_ERROR("DRM file descriptors open either dying or " +				  "closing file descriptors\n"); +			DRM_ERROR("while having the lock. I will not reclaim buffers.\n"); +			DRM_ERROR("Locking context is 0x%08x\n", +				  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));  		} +	} else if (drm_i_have_hw_lock(filp)) { +		DRM_DEBUG("File %p released, freeing lock for context %d\n", +			  filp, _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + +		drm_lock_free(dev, &dev->lock.hw_lock->lock, +			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));  	} +  	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) &&  	    !dev->driver->reclaim_buffers_locked) {  		dev->driver->reclaim_buffers(dev, filp); @@ -414,6 +471,7 @@ int drm_release(struct inode *inode, struct file *filp)  	drm_fasync(-1, filp, 0);  	mutex_lock(&dev->ctxlist_mutex); +  	if (dev->ctxlist && (!list_empty(&dev->ctxlist->head))) {  		drm_ctx_list_t *pos, *n; @@ -435,6 +493,7 @@ int drm_release(struct inode *inode, struct file *filp)  	mutex_unlock(&dev->ctxlist_mutex);  	mutex_lock(&dev->struct_mutex); +	drm_object_release(filp);  	if (priv->remove_auth_on_close == 1) {  		drm_file_t *temp = dev->file_first;  		while (temp) { | 
