Commit 7c1c2871 authored by Dave Airlie's avatar Dave Airlie Committed by Dave Airlie
Browse files

drm: move to kref per-master structures.



This is step one towards having multiple masters sharing a drm
device in order to get fast-user-switching to work.

It splits out the information associated with the drm master
into a separate kref counted structure, and allocates this when
a master opens the device node. It also allows the current master
to abdicate (say while VT switched), and a new master to take over
the hardware.

It moves the Intel and radeon drivers to using the sarea from
within the new master structures.

Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent e7f7ab45
Loading
Loading
Loading
Loading
+15 −14
Original line number Diff line number Diff line
@@ -45,14 +45,15 @@
 * the one with matching magic number, while holding the drm_device::struct_mutex
 * lock.
 */
static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic)
static struct drm_file *drm_find_file(struct drm_master *master, drm_magic_t magic)
{
	struct drm_file *retval = NULL;
	struct drm_magic_entry *pt;
	struct drm_hash_item *hash;
	struct drm_device *dev = master->minor->dev;

	mutex_lock(&dev->struct_mutex);
	if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
	if (!drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
		pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
		retval = pt->priv;
	}
@@ -71,11 +72,11 @@ static struct drm_file *drm_find_file(struct drm_device * dev, drm_magic_t magic
 * associated the magic number hash key in drm_device::magiclist, while holding
 * the drm_device::struct_mutex lock.
 */
static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
static int drm_add_magic(struct drm_master *master, struct drm_file *priv,
			 drm_magic_t magic)
{
	struct drm_magic_entry *entry;

	struct drm_device *dev = master->minor->dev;
	DRM_DEBUG("%d\n", magic);

	entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
@@ -83,11 +84,10 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
		return -ENOMEM;
	memset(entry, 0, sizeof(*entry));
	entry->priv = priv;

	entry->hash_item.key = (unsigned long)magic;
	mutex_lock(&dev->struct_mutex);
	drm_ht_insert_item(&dev->magiclist, &entry->hash_item);
	list_add_tail(&entry->head, &dev->magicfree);
	drm_ht_insert_item(&master->magiclist, &entry->hash_item);
	list_add_tail(&entry->head, &master->magicfree);
	mutex_unlock(&dev->struct_mutex);

	return 0;
@@ -102,20 +102,21 @@ static int drm_add_magic(struct drm_device * dev, struct drm_file * priv,
 * Searches and unlinks the entry in drm_device::magiclist with the magic
 * number hash key, while holding the drm_device::struct_mutex lock.
 */
static int drm_remove_magic(struct drm_device * dev, drm_magic_t magic)
static int drm_remove_magic(struct drm_master *master, drm_magic_t magic)
{
	struct drm_magic_entry *pt;
	struct drm_hash_item *hash;
	struct drm_device *dev = master->minor->dev;

	DRM_DEBUG("%d\n", magic);

	mutex_lock(&dev->struct_mutex);
	if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
	if (drm_ht_find_item(&master->magiclist, (unsigned long)magic, &hash)) {
		mutex_unlock(&dev->struct_mutex);
		return -EINVAL;
	}
	pt = drm_hash_entry(hash, struct drm_magic_entry, hash_item);
	drm_ht_remove_item(&dev->magiclist, hash);
	drm_ht_remove_item(&master->magiclist, hash);
	list_del(&pt->head);
	mutex_unlock(&dev->struct_mutex);

@@ -153,9 +154,9 @@ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
				++sequence;	/* reserve 0 */
			auth->magic = sequence++;
			spin_unlock(&lock);
		} while (drm_find_file(dev, auth->magic));
		} while (drm_find_file(file_priv->master, auth->magic));
		file_priv->magic = auth->magic;
		drm_add_magic(dev, file_priv, auth->magic);
		drm_add_magic(file_priv->master, file_priv, auth->magic);
	}

	DRM_DEBUG("%u\n", auth->magic);
@@ -181,9 +182,9 @@ int drm_authmagic(struct drm_device *dev, void *data,
	struct drm_file *file;

	DRM_DEBUG("%u\n", auth->magic);
	if ((file = drm_find_file(dev, auth->magic))) {
	if ((file = drm_find_file(file_priv->master, auth->magic))) {
		file->authenticated = 1;
		drm_remove_magic(dev, auth->magic);
		drm_remove_magic(file_priv->master, auth->magic);
		return 0;
	}
	return -EINVAL;
+15 −5
Original line number Diff line number Diff line
@@ -54,9 +54,9 @@ static struct drm_map_list *drm_find_matching_map(struct drm_device *dev,
{
	struct drm_map_list *entry;
	list_for_each_entry(entry, &dev->maplist, head) {
		if (entry->map && map->type == entry->map->type &&
		if (entry->map && (entry->master == dev->primary->master) && (map->type == entry->map->type) &&
		    ((entry->map->offset == map->offset) ||
		     (map->type == _DRM_SHM && map->flags==_DRM_CONTAINS_LOCK))) {
		     ((map->type == _DRM_SHM) && (map->flags&_DRM_CONTAINS_LOCK)))) {
			return entry;
		}
	}
@@ -210,12 +210,12 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
		map->offset = (unsigned long)map->handle;
		if (map->flags & _DRM_CONTAINS_LOCK) {
			/* Prevent a 2nd X Server from creating a 2nd lock */
			if (dev->lock.hw_lock != NULL) {
			if (dev->primary->master->lock.hw_lock != NULL) {
				vfree(map->handle);
				drm_free(map, sizeof(*map), DRM_MEM_MAPS);
				return -EBUSY;
			}
			dev->sigdata.lock = dev->lock.hw_lock = map->handle;	/* Pointer to lock */
			dev->sigdata.lock = dev->primary->master->lock.hw_lock = map->handle;	/* Pointer to lock */
		}
		break;
	case _DRM_AGP: {
@@ -319,6 +319,7 @@ static int drm_addmap_core(struct drm_device * dev, unsigned int offset,
	list->user_token = list->hash.key << PAGE_SHIFT;
	mutex_unlock(&dev->struct_mutex);

	list->master = dev->primary->master;
	*maplist = list;
	return 0;
	}
@@ -345,7 +346,7 @@ int drm_addmap_ioctl(struct drm_device *dev, void *data,
	struct drm_map_list *maplist;
	int err;

	if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP))
	if (!(capable(CAP_SYS_ADMIN) || map->type == _DRM_AGP || map->type == _DRM_SHM))
		return -EPERM;

	err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -380,10 +381,12 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
	struct drm_map_list *r_list = NULL, *list_t;
	drm_dma_handle_t dmah;
	int found = 0;
	struct drm_master *master;

	/* Find the list entry for the map and remove it */
	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
		if (r_list->map == map) {
			master = r_list->master;
			list_del(&r_list->head);
			drm_ht_remove_key(&dev->map_hash,
					  r_list->user_token >> PAGE_SHIFT);
@@ -409,6 +412,13 @@ int drm_rmmap_locked(struct drm_device *dev, drm_local_map_t *map)
		break;
	case _DRM_SHM:
		vfree(map->handle);
		if (master) {
			if (dev->sigdata.lock == master->lock.hw_lock)
				dev->sigdata.lock = NULL;
			master->lock.hw_lock = NULL;   /* SHM removed */
			master->lock.file_priv = NULL;
			wake_up_interruptible(&master->lock.lock_queue);
		}
		break;
	case _DRM_AGP:
	case _DRM_SCATTER_GATHER:
+4 −6
Original line number Diff line number Diff line
@@ -256,12 +256,13 @@ static int drm_context_switch(struct drm_device * dev, int old, int new)
 * hardware lock is held, clears the drm_device::context_flag and wakes up
 * drm_device::context_wait.
 */
static int drm_context_switch_complete(struct drm_device * dev, int new)
static int drm_context_switch_complete(struct drm_device *dev,
				       struct drm_file *file_priv, int new)
{
	dev->last_context = new;	/* PRE/POST: This is the _only_ writer. */
	dev->last_switch = jiffies;

	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
	if (!_DRM_LOCK_IS_HELD(file_priv->master->lock.hw_lock->lock)) {
		DRM_ERROR("Lock isn't held after context switch\n");
	}

@@ -420,7 +421,7 @@ int drm_newctx(struct drm_device *dev, void *data,
	struct drm_ctx *ctx = data;

	DRM_DEBUG("%d\n", ctx->handle);
	drm_context_switch_complete(dev, ctx->handle);
	drm_context_switch_complete(dev, file_priv, ctx->handle);

	return 0;
}
@@ -442,9 +443,6 @@ int drm_rmctx(struct drm_device *dev, void *data,
	struct drm_ctx *ctx = data;

	DRM_DEBUG("%d\n", ctx->handle);
	if (ctx->handle == DRM_KERNEL_CONTEXT + 1) {
		file_priv->remove_auth_on_close = 1;
	}
	if (ctx->handle != DRM_KERNEL_CONTEXT) {
		if (dev->driver->context_dtor)
			dev->driver->context_dtor(dev, ctx->handle);
+3 −30
Original line number Diff line number Diff line
@@ -74,6 +74,9 @@ static struct drm_ioctl_desc drm_ioctls[] = {
	DRM_IOCTL_DEF(DRM_IOCTL_SET_SAREA_CTX, drm_setsareactx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_IOCTL_GET_SAREA_CTX, drm_getsareactx, DRM_AUTH),

	DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),

	DRM_IOCTL_DEF(DRM_IOCTL_ADD_CTX, drm_addctx, DRM_AUTH|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_IOCTL_RM_CTX, drm_rmctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_IOCTL_MOD_CTX, drm_modctx, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
@@ -138,8 +141,6 @@ static struct drm_ioctl_desc drm_ioctls[] = {
 */
int drm_lastclose(struct drm_device * dev)
{
	struct drm_magic_entry *pt, *next;
	struct drm_map_list *r_list, *list_t;
	struct drm_vma_entry *vma, *vma_temp;
	int i;

@@ -149,12 +150,6 @@ int drm_lastclose(struct drm_device * dev)
		dev->driver->lastclose(dev);
	DRM_DEBUG("driver lastclose completed\n");

	if (dev->unique) {
		drm_free(dev->unique, strlen(dev->unique) + 1, DRM_MEM_DRIVER);
		dev->unique = NULL;
		dev->unique_len = 0;
	}

	if (dev->irq_enabled)
		drm_irq_uninstall(dev);

@@ -164,16 +159,6 @@ int drm_lastclose(struct drm_device * dev)
	drm_drawable_free_all(dev);
	del_timer(&dev->timer);

	/* Clear pid list */
	if (dev->magicfree.next) {
		list_for_each_entry_safe(pt, next, &dev->magicfree, head) {
			list_del(&pt->head);
			drm_ht_remove_item(&dev->magiclist, &pt->hash_item);
			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
		}
		drm_ht_remove(&dev->magiclist);
	}

	/* Clear AGP information */
	if (drm_core_has_AGP(dev) && dev->agp) {
		struct drm_agp_mem *entry, *tempe;
@@ -205,13 +190,6 @@ int drm_lastclose(struct drm_device * dev)
		drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
	}

	list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) {
		if (!(r_list->map->flags & _DRM_DRIVER)) {
			drm_rmmap_locked(dev, r_list->map);
			r_list = NULL;
		}
	}

	if (drm_core_check_feature(dev, DRIVER_DMA_QUEUE) && dev->queuelist) {
		for (i = 0; i < dev->queue_count; i++) {
			if (dev->queuelist[i]) {
@@ -231,11 +209,6 @@ int drm_lastclose(struct drm_device * dev)
	if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
		drm_dma_takedown(dev);

	if (dev->lock.hw_lock) {
		dev->sigdata.lock = dev->lock.hw_lock = NULL;	/* SHM removed */
		dev->lock.file_priv = NULL;
		wake_up_interruptible(&dev->lock.lock_queue);
	}
	mutex_unlock(&dev->struct_mutex);

	DRM_DEBUG("lastclose completed\n");
+124 −77
Original line number Diff line number Diff line
@@ -44,10 +44,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,

static int drm_setup(struct drm_device * dev)
{
	drm_local_map_t *map;
	int i;
	int ret;
	u32 sareapage;

	if (dev->driver->firstopen) {
		ret = dev->driver->firstopen(dev);
@@ -55,14 +53,6 @@ static int drm_setup(struct drm_device * dev)
			return ret;
	}

	dev->magicfree.next = NULL;

	/* prebuild the SAREA */
	sareapage = max_t(unsigned, SAREA_MAX, PAGE_SIZE);
	i = drm_addmap(dev, 0, sareapage, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
	if (i != 0)
		return i;

	atomic_set(&dev->ioctl_count, 0);
	atomic_set(&dev->vma_count, 0);
	dev->buf_use = 0;
@@ -77,16 +67,12 @@ static int drm_setup(struct drm_device * dev)
	for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
		atomic_set(&dev->counts[i], 0);

	drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER);
	INIT_LIST_HEAD(&dev->magicfree);

	dev->sigdata.lock = NULL;
	init_waitqueue_head(&dev->lock.lock_queue);

	dev->queue_count = 0;
	dev->queue_reserved = 0;
	dev->queue_slots = 0;
	dev->queuelist = NULL;
	dev->irq_enabled = 0;
	dev->context_flag = 0;
	dev->interrupt_flag = 0;
	dev->dma_flag = 0;
@@ -265,10 +251,42 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
			goto out_free;
	}


	/* if there is no current master make this fd it */
	mutex_lock(&dev->struct_mutex);
	if (!priv->minor->master) {
		/* create a new master */
		priv->minor->master = drm_master_create(priv->minor);
		if (!priv->minor->master) {
			ret = -ENOMEM;
			goto out_free;
		}

		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);
	if (list_empty(&dev->filelist))
		priv->master = 1;
				/* 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;
			}
		}
	} else {
		/* get a reference to the master */
		priv->master = drm_master_get(priv->minor->master);
		mutex_unlock(&dev->struct_mutex);
	}

	mutex_lock(&dev->struct_mutex);
	list_add(&priv->lhead, &dev->filelist);
	mutex_unlock(&dev->struct_mutex);

@@ -314,61 +332,33 @@ int drm_fasync(int fd, struct file *filp, int on)
}
EXPORT_SYMBOL(drm_fasync);

/**
 * Release file.
 *
 * \param inode device inode
 * \param file_priv DRM file private.
 * \return zero on success or a negative number on failure.
 *
 * If the hardware lock is held then free it, and take it again for the kernel
 * context since it's necessary to reclaim buffers. Unlink the file private
 * data from its list and free it. Decreases the open count and if it reaches
 * zero calls drm_lastclose().
/*
 * Reclaim locked buffers; note that this may be a bad idea if the current
 * context doesn't have the hw lock...
 */
int drm_release(struct inode *inode, struct file *filp)
static void drm_reclaim_locked_buffers(struct drm_device *dev, struct file *f)
{
	struct drm_file *file_priv = filp->private_data;
	struct drm_device *dev = file_priv->minor->dev;
	int retcode = 0;

	lock_kernel();
	struct drm_file *file_priv = f->private_data;

	DRM_DEBUG("open_count = %d\n", dev->open_count);

	if (dev->driver->preclose)
		dev->driver->preclose(dev, file_priv);

	/* ========================================================
	 * Begin inline drm_release
	 */

	DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
		  task_pid_nr(current),
		  (long)old_encode_dev(file_priv->minor->device),
		  dev->open_count);

	if (dev->driver->reclaim_buffers_locked && dev->lock.hw_lock) {
	if (drm_i_have_hw_lock(dev, file_priv)) {
		dev->driver->reclaim_buffers_locked(dev, file_priv);
	} else {
			unsigned long endtime = jiffies + 3 * DRM_HZ;
		unsigned long _end = jiffies + 3 * DRM_HZ;
		int locked = 0;

			drm_idlelock_take(&dev->lock);
		drm_idlelock_take(&file_priv->master->lock);

		/*
		 * Wait for a while.
		 */

		do {
				spin_lock_bh(&dev->lock.spinlock);
				locked = dev->lock.idle_has_lock;
				spin_unlock_bh(&dev->lock.spinlock);
			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, endtime));
		} while (!time_after_eq(jiffies, _end));

		if (!locked) {
			DRM_ERROR("reclaim_buffers_locked() deadlock. Please rework this\n"
@@ -377,31 +367,76 @@ int drm_release(struct inode *inode, struct file *filp)
		}

		dev->driver->reclaim_buffers_locked(dev, file_priv);
			drm_idlelock_release(&dev->lock);
		drm_idlelock_release(&file_priv->master->lock);
	}
}

	if (dev->driver->reclaim_buffers_idlelocked && dev->lock.hw_lock) {
static void drm_master_release(struct drm_device *dev, struct file *filp)
{
	struct drm_file *file_priv = filp->private_data;

		drm_idlelock_take(&dev->lock);
		dev->driver->reclaim_buffers_idlelocked(dev, file_priv);
		drm_idlelock_release(&dev->lock);
	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(dev->lock.hw_lock->lock));

		drm_lock_free(&dev->lock,
			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
			  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);
	}
}

/**
 * Release file.
 *
 * \param inode device inode
 * \param file_priv DRM file private.
 * \return zero on success or a negative number on failure.
 *
 * If the hardware lock is held then free it, and take it again for the kernel
 * context since it's necessary to reclaim buffers. Unlink the file private
 * data from its list and free it. Decreases the open count and if it reaches
 * zero calls drm_lastclose().
 */
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;
	int retcode = 0;

	lock_kernel();

	DRM_DEBUG("open_count = %d\n", dev->open_count);

	if (dev->driver->preclose)
		dev->driver->preclose(dev, file_priv);

	/* ========================================================
	 * Begin inline drm_release
	 */

	DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n",
		  task_pid_nr(current),
		  (long)old_encode_dev(file_priv->minor->device),
		  dev->open_count);

	/* if the master has gone away we can't do anything with the lock */
	if (file_priv->minor->master)
		drm_master_release(dev, filp);

	if (dev->driver->driver_features & DRIVER_GEM)
		drm_gem_release(dev, file_priv);
@@ -428,12 +463,24 @@ int drm_release(struct inode *inode, struct file *filp)
	mutex_unlock(&dev->ctxlist_mutex);

	mutex_lock(&dev->struct_mutex);
	if (file_priv->remove_auth_on_close == 1) {
		struct drm_file *temp;

		list_for_each_entry(temp, &dev->filelist, lhead)
	if (file_priv->is_master) {
		struct drm_file *temp;
		list_for_each_entry(temp, &dev->filelist, lhead) {
			if ((temp->master == file_priv->master) &&
			    (temp != file_priv))
				temp->authenticated = 0;
		}

		if (file_priv->minor->master == file_priv->master) {
			/* drop the reference held my the minor */
			drm_master_put(&file_priv->minor->master);
		}
	}

	/* drop the reference held my the file priv */
	drm_master_put(&file_priv->master);
	file_priv->is_master = 0;
	list_del(&file_priv->lhead);
	mutex_unlock(&dev->struct_mutex);

@@ -448,9 +495,9 @@ int drm_release(struct inode *inode, struct file *filp)
	atomic_inc(&dev->counts[_DRM_STAT_CLOSES]);
	spin_lock(&dev->count_lock);
	if (!--dev->open_count) {
		if (atomic_read(&dev->ioctl_count) || dev->blocked) {
			DRM_ERROR("Device busy: %d %d\n",
				  atomic_read(&dev->ioctl_count), dev->blocked);
		if (atomic_read(&dev->ioctl_count)) {
			DRM_ERROR("Device busy: %d\n",
				  atomic_read(&dev->ioctl_count));
			spin_unlock(&dev->count_lock);
			unlock_kernel();
			return -EBUSY;
Loading