Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7846b12f authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'drm-vmwgfx-next' of git://people.freedesktop.org/~syeh/repos_linux into drm-next

vmwgfx add fence fd support.

* 'drm-vmwgfx-next' of git://people.freedesktop.org/~syeh/repos_linux:
  drm/vmwgfx: Bump the version for fence FD support
  drm/vmwgfx: Add export fence to file descriptor support
  drm/vmwgfx: Add support for imported Fence File Descriptor
  drm/vmwgfx: Prepare to support fence fd
  drm/vmwgfx: Fix incorrect command header offset at restart
  drm/vmwgfx: Support the NOP_ERROR command
  drm/vmwgfx: Restart command buffers after errors
  drm/vmwgfx: Move irq bottom half processing to threads
  drm/vmwgfx: Don't use drm_irq_[un]install
parents 7ebdb0dd d78acfe9
Loading
Loading
Loading
Loading
+178 −64
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ struct vmw_cmdbuf_context {
	struct list_head hw_submitted;
	struct list_head preempted;
	unsigned num_hw_submitted;
	bool block_submission;
};

/**
@@ -60,6 +61,9 @@ struct vmw_cmdbuf_context {
 * kernel command submissions, @cur.
 * @space_mutex: Mutex to protect against starvation when we allocate
 * main pool buffer space.
 * @error_mutex: Mutex to serialize the work queue error handling.
 * Note this is not needed if the same workqueue handler
 * can't race with itself...
 * @work: A struct work_struct implementeing command buffer error handling.
 * Immutable.
 * @dev_priv: Pointer to the device private struct. Immutable.
@@ -85,7 +89,6 @@ struct vmw_cmdbuf_context {
 * Internal protection.
 * @dheaders: Pool of DMA memory for device command buffer headers with trailing
 * space for inline data. Internal protection.
 * @tasklet: Tasklet struct for irq processing. Immutable.
 * @alloc_queue: Wait queue for processes waiting to allocate command buffer
 * space.
 * @idle_queue: Wait queue for processes waiting for command buffer idle.
@@ -102,6 +105,7 @@ struct vmw_cmdbuf_context {
struct vmw_cmdbuf_man {
	struct mutex cur_mutex;
	struct mutex space_mutex;
	struct mutex error_mutex;
	struct work_struct work;
	struct vmw_private *dev_priv;
	struct vmw_cmdbuf_context ctx[SVGA_CB_CONTEXT_MAX];
@@ -117,7 +121,6 @@ struct vmw_cmdbuf_man {
	spinlock_t lock;
	struct dma_pool *headers;
	struct dma_pool *dheaders;
	struct tasklet_struct tasklet;
	wait_queue_head_t alloc_queue;
	wait_queue_head_t idle_queue;
	bool irq_on;
@@ -185,8 +188,9 @@ struct vmw_cmdbuf_alloc_info {
	for (_i = 0, _ctx = &(_man)->ctx[0]; (_i) < SVGA_CB_CONTEXT_MAX; \
	     ++(_i), ++(_ctx))

static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, bool enable);

static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
				bool enable);
static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context);

/**
 * vmw_cmdbuf_cur_lock - Helper to lock the cur_mutex.
@@ -278,9 +282,9 @@ void vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header)
		vmw_cmdbuf_header_inline_free(header);
		return;
	}
	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	__vmw_cmdbuf_header_free(header);
	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);
}


@@ -331,7 +335,8 @@ static void vmw_cmdbuf_ctx_submit(struct vmw_cmdbuf_man *man,
				  struct vmw_cmdbuf_context *ctx)
{
	while (ctx->num_hw_submitted < man->max_hw_submitted &&
	      !list_empty(&ctx->submitted)) {
	       !list_empty(&ctx->submitted) &&
	       !ctx->block_submission) {
		struct vmw_cmdbuf_header *entry;
		SVGACBStatus status;

@@ -386,12 +391,17 @@ static void vmw_cmdbuf_ctx_process(struct vmw_cmdbuf_man *man,
			__vmw_cmdbuf_header_free(entry);
			break;
		case SVGA_CB_STATUS_COMMAND_ERROR:
		case SVGA_CB_STATUS_CB_HEADER_ERROR:
			entry->cb_header->status = SVGA_CB_STATUS_NONE;
			list_add_tail(&entry->list, &man->error);
			schedule_work(&man->work);
			break;
		case SVGA_CB_STATUS_PREEMPTED:
			list_add(&entry->list, &ctx->preempted);
			entry->cb_header->status = SVGA_CB_STATUS_NONE;
			list_add_tail(&entry->list, &ctx->preempted);
			break;
		case SVGA_CB_STATUS_CB_HEADER_ERROR:
			WARN_ONCE(true, "Command buffer header error.\n");
			__vmw_cmdbuf_header_free(entry);
			break;
		default:
			WARN_ONCE(true, "Undefined command buffer status.\n");
@@ -468,20 +478,17 @@ static void vmw_cmdbuf_ctx_add(struct vmw_cmdbuf_man *man,
}

/**
 * vmw_cmdbuf_man_tasklet - The main part of the command buffer interrupt
 * handler implemented as a tasklet.
 * vmw_cmdbuf_irqthread - The main part of the command buffer interrupt
 * handler implemented as a threaded irq task.
 *
 * @data: Tasklet closure. A pointer to the command buffer manager cast to
 * an unsigned long.
 * @man: Pointer to the command buffer manager.
 *
 * The bottom half (tasklet) of the interrupt handler simply calls into the
 * The bottom half of the interrupt handler simply calls into the
 * command buffer processor to free finished buffers and submit any
 * queued buffers to hardware.
 */
static void vmw_cmdbuf_man_tasklet(unsigned long data)
void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man)
{
	struct vmw_cmdbuf_man *man = (struct vmw_cmdbuf_man *) data;

	spin_lock(&man->lock);
	vmw_cmdbuf_man_process(man);
	spin_unlock(&man->lock);
@@ -502,24 +509,112 @@ static void vmw_cmdbuf_work_func(struct work_struct *work)
		container_of(work, struct vmw_cmdbuf_man, work);
	struct vmw_cmdbuf_header *entry, *next;
	uint32_t dummy;
	bool restart = false;
	bool restart[SVGA_CB_CONTEXT_MAX];
	bool send_fence = false;
	struct list_head restart_head[SVGA_CB_CONTEXT_MAX];
	int i;
	struct vmw_cmdbuf_context *ctx;

	spin_lock_bh(&man->lock);
	for_each_cmdbuf_ctx(man, i, ctx) {
		INIT_LIST_HEAD(&restart_head[i]);
		restart[i] = false;
	}

	mutex_lock(&man->error_mutex);
	spin_lock(&man->lock);
	list_for_each_entry_safe(entry, next, &man->error, list) {
		restart = true;
		DRM_ERROR("Command buffer error.\n");
		SVGACBHeader *cb_hdr = entry->cb_header;
		SVGA3dCmdHeader *header = (SVGA3dCmdHeader *)
			(entry->cmd + cb_hdr->errorOffset);
		u32 error_cmd_size, new_start_offset;
		const char *cmd_name;

		list_del_init(&entry->list);
		restart[entry->cb_context] = true;

		if (!vmw_cmd_describe(header, &error_cmd_size, &cmd_name)) {
			DRM_ERROR("Unknown command causing device error.\n");
			DRM_ERROR("Command buffer offset is %lu\n",
				  (unsigned long) cb_hdr->errorOffset);
			__vmw_cmdbuf_header_free(entry);
			send_fence = true;
			continue;
		}

		list_del(&entry->list);
		DRM_ERROR("Command \"%s\" causing device error.\n", cmd_name);
		DRM_ERROR("Command buffer offset is %lu\n",
			  (unsigned long) cb_hdr->errorOffset);
		DRM_ERROR("Command size is %lu\n",
			  (unsigned long) error_cmd_size);

		new_start_offset = cb_hdr->errorOffset + error_cmd_size;

		if (new_start_offset >= cb_hdr->length) {
			__vmw_cmdbuf_header_free(entry);
		wake_up_all(&man->idle_queue);
			send_fence = true;
			continue;
		}
	spin_unlock_bh(&man->lock);

	if (restart && vmw_cmdbuf_startstop(man, true))
		DRM_ERROR("Failed restarting command buffer context 0.\n");
		if (man->using_mob)
			cb_hdr->ptr.mob.mobOffset += new_start_offset;
		else
			cb_hdr->ptr.pa += (u64) new_start_offset;

		entry->cmd += new_start_offset;
		cb_hdr->length -= new_start_offset;
		cb_hdr->errorOffset = 0;
		cb_hdr->offset = 0;
		list_add_tail(&entry->list, &restart_head[entry->cb_context]);
		man->ctx[entry->cb_context].block_submission = true;
	}
	spin_unlock(&man->lock);

	/* Preempt all contexts with errors */
	for_each_cmdbuf_ctx(man, i, ctx) {
		if (ctx->block_submission && vmw_cmdbuf_preempt(man, i))
			DRM_ERROR("Failed preempting command buffer "
				  "context %u.\n", i);
	}

	spin_lock(&man->lock);
	for_each_cmdbuf_ctx(man, i, ctx) {
		if (!ctx->block_submission)
			continue;

		/* Move preempted command buffers to the preempted queue. */
		vmw_cmdbuf_ctx_process(man, ctx, &dummy);

		/*
		 * Add the preempted queue after the command buffer
		 * that caused an error.
		 */
		list_splice_init(&ctx->preempted, restart_head[i].prev);

		/*
		 * Finally add all command buffers first in the submitted
		 * queue, to rerun them.
		 */
		list_splice_init(&restart_head[i], &ctx->submitted);

		ctx->block_submission = false;
	}

	vmw_cmdbuf_man_process(man);
	spin_unlock(&man->lock);

	for_each_cmdbuf_ctx(man, i, ctx) {
		if (restart[i] && vmw_cmdbuf_startstop(man, i, true))
			DRM_ERROR("Failed restarting command buffer "
				  "context %u.\n", i);
	}

	/* Send a new fence in case one was removed */
	if (send_fence) {
		vmw_fifo_send_fence(man->dev_priv, &dummy);
		wake_up_all(&man->idle_queue);
	}

	mutex_unlock(&man->error_mutex);
}

/**
@@ -536,7 +631,7 @@ static bool vmw_cmdbuf_man_idle(struct vmw_cmdbuf_man *man,
	bool idle = false;
	int i;

	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	vmw_cmdbuf_man_process(man);
	for_each_cmdbuf_ctx(man, i, ctx) {
		if (!list_empty(&ctx->submitted) ||
@@ -548,7 +643,7 @@ static bool vmw_cmdbuf_man_idle(struct vmw_cmdbuf_man *man,
	idle = list_empty(&man->error);

out_unlock:
	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);

	return idle;
}
@@ -571,7 +666,7 @@ static void __vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man)
	if (!cur)
		return;

	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	if (man->cur_pos == 0) {
		__vmw_cmdbuf_header_free(cur);
		goto out_unlock;
@@ -580,7 +675,7 @@ static void __vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man)
	man->cur->cb_header->length = man->cur_pos;
	vmw_cmdbuf_ctx_add(man, man->cur, SVGA_CB_CONTEXT_0);
out_unlock:
	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);
	man->cur = NULL;
	man->cur_pos = 0;
}
@@ -673,14 +768,14 @@ static bool vmw_cmdbuf_try_alloc(struct vmw_cmdbuf_man *man,
		return true;
 
	memset(info->node, 0, sizeof(*info->node));
	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
	if (ret) {
		vmw_cmdbuf_man_process(man);
		ret = drm_mm_insert_node(&man->mm, info->node, info->page_size);
	}

	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);
	info->done = !ret;

	return info->done;
@@ -801,9 +896,9 @@ static int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man,
	return 0;

out_no_cb_header:
	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	drm_mm_remove_node(&header->node);
	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);

	return ret;
}
@@ -1023,18 +1118,6 @@ void vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size,
	vmw_cmdbuf_cur_unlock(man);
}

/**
 * vmw_cmdbuf_tasklet_schedule - Schedule the interrupt handler bottom half.
 *
 * @man: The command buffer manager.
 */
void vmw_cmdbuf_tasklet_schedule(struct vmw_cmdbuf_man *man)
{
	if (!man)
		return;

	tasklet_schedule(&man->tasklet);
}

/**
 * vmw_cmdbuf_send_device_command - Send a command through the device context.
@@ -1059,9 +1142,9 @@ static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man,
	memcpy(cmd, command, size);
	header->cb_header->length = size;
	header->cb_context = SVGA_CB_CONTEXT_DEVICE;
	spin_lock_bh(&man->lock);
	spin_lock(&man->lock);
	status = vmw_cmdbuf_header_submit(header);
	spin_unlock_bh(&man->lock);
	spin_unlock(&man->lock);
	vmw_cmdbuf_header_free(header);

	if (status != SVGA_CB_STATUS_COMPLETED) {
@@ -1073,6 +1156,29 @@ static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man,
	return 0;
}

/**
 * vmw_cmdbuf_preempt - Send a preempt command through the device
 * context.
 *
 * @man: The command buffer manager.
 *
 * Synchronously sends a preempt command.
 */
static int vmw_cmdbuf_preempt(struct vmw_cmdbuf_man *man, u32 context)
{
	struct {
		uint32 id;
		SVGADCCmdPreempt body;
	} __packed cmd;

	cmd.id = SVGA_DC_CMD_PREEMPT;
	cmd.body.context = SVGA_CB_CONTEXT_0 + context;
	cmd.body.ignoreIDZero = 0;

	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
}


/**
 * vmw_cmdbuf_startstop - Send a start / stop command through the device
 * context.
@@ -1082,7 +1188,7 @@ static int vmw_cmdbuf_send_device_command(struct vmw_cmdbuf_man *man,
 *
 * Synchronously sends a device start / stop context command.
 */
static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man,
static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man, u32 context,
				bool enable)
{
	struct {
@@ -1092,7 +1198,7 @@ static int vmw_cmdbuf_startstop(struct vmw_cmdbuf_man *man,

	cmd.id = SVGA_DC_CMD_START_STOP_CONTEXT;
	cmd.body.enable = (enable) ? 1 : 0;
	cmd.body.context = SVGA_CB_CONTEXT_0;
	cmd.body.context = SVGA_CB_CONTEXT_0 + context;

	return vmw_cmdbuf_send_device_command(man, &cmd, sizeof(cmd));
}
@@ -1191,7 +1297,7 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
{
	struct vmw_cmdbuf_man *man;
	struct vmw_cmdbuf_context *ctx;
	int i;
	unsigned int i;
	int ret;

	if (!(dev_priv->capabilities & SVGA_CAP_COMMAND_BUFFERS))
@@ -1226,8 +1332,7 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
	spin_lock_init(&man->lock);
	mutex_init(&man->cur_mutex);
	mutex_init(&man->space_mutex);
	tasklet_init(&man->tasklet, vmw_cmdbuf_man_tasklet,
		     (unsigned long) man);
	mutex_init(&man->error_mutex);
	man->default_size = VMW_CMDBUF_INLINE_SIZE;
	init_waitqueue_head(&man->alloc_queue);
	init_waitqueue_head(&man->idle_queue);
@@ -1236,12 +1341,15 @@ struct vmw_cmdbuf_man *vmw_cmdbuf_man_create(struct vmw_private *dev_priv)
	INIT_WORK(&man->work, &vmw_cmdbuf_work_func);
	vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_ERROR,
			       &dev_priv->error_waiters);
	ret = vmw_cmdbuf_startstop(man, true);
	for_each_cmdbuf_ctx(man, i, ctx) {
		ret = vmw_cmdbuf_startstop(man, i, true);
		if (ret) {
		DRM_ERROR("Failed starting command buffer context 0.\n");
			DRM_ERROR("Failed starting command buffer "
				  "context %u.\n", i);
			vmw_cmdbuf_man_destroy(man);
			return ERR_PTR(ret);
		}
	}

	return man;

@@ -1290,18 +1398,24 @@ void vmw_cmdbuf_remove_pool(struct vmw_cmdbuf_man *man)
 */
void vmw_cmdbuf_man_destroy(struct vmw_cmdbuf_man *man)
{
	struct vmw_cmdbuf_context *ctx;
	unsigned int i;

	WARN_ON_ONCE(man->has_pool);
	(void) vmw_cmdbuf_idle(man, false, 10*HZ);
	if (vmw_cmdbuf_startstop(man, false))
		DRM_ERROR("Failed stopping command buffer context 0.\n");

	for_each_cmdbuf_ctx(man, i, ctx)
		if (vmw_cmdbuf_startstop(man, i, false))
			DRM_ERROR("Failed stopping command buffer "
				  "context %u.\n", i);

	vmw_generic_waiter_remove(man->dev_priv, SVGA_IRQFLAG_ERROR,
				  &man->dev_priv->error_waiters);
	tasklet_kill(&man->tasklet);
	(void) cancel_work_sync(&man->work);
	dma_pool_destroy(man->dheaders);
	dma_pool_destroy(man->headers);
	mutex_destroy(&man->cur_mutex);
	mutex_destroy(&man->space_mutex);
	mutex_destroy(&man->error_mutex);
	kfree(man);
}
+3 −8
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@
#include <drm/ttm/ttm_module.h>
#include <linux/dma_remapping.h>

#define VMWGFX_DRIVER_NAME "vmwgfx"
#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
#define VMWGFX_CHIP_SVGAII 0
#define VMW_FB_RESERVATION 0
@@ -825,7 +824,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
	}

	if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
		ret = drm_irq_install(dev, dev->pdev->irq);
		ret = vmw_irq_install(dev, dev->pdev->irq);
		if (ret != 0) {
			DRM_ERROR("Failed installing irq: %d\n", ret);
			goto out_no_irq;
@@ -937,7 +936,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
	vmw_fence_manager_takedown(dev_priv->fman);
out_no_fman:
	if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
		drm_irq_uninstall(dev_priv->dev);
		vmw_irq_uninstall(dev_priv->dev);
out_no_irq:
	if (dev_priv->stealth)
		pci_release_region(dev->pdev, 2);
@@ -990,7 +989,7 @@ static void vmw_driver_unload(struct drm_device *dev)
	vmw_release_device_late(dev_priv);
	vmw_fence_manager_takedown(dev_priv->fman);
	if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
		drm_irq_uninstall(dev_priv->dev);
		vmw_irq_uninstall(dev_priv->dev);
	if (dev_priv->stealth)
		pci_release_region(dev->pdev, 2);
	else
@@ -1516,10 +1515,6 @@ static struct drm_driver driver = {
	.load = vmw_driver_load,
	.unload = vmw_driver_unload,
	.lastclose = vmw_lastclose,
	.irq_preinstall = vmw_irq_preinstall,
	.irq_postinstall = vmw_irq_postinstall,
	.irq_uninstall = vmw_irq_uninstall,
	.irq_handler = vmw_irq_handler,
	.get_vblank_counter = vmw_get_vblank_counter,
	.enable_vblank = vmw_enable_vblank,
	.disable_vblank = vmw_disable_vblank,
+23 −16
Original line number Diff line number Diff line
@@ -40,10 +40,12 @@
#include <drm/ttm/ttm_execbuf_util.h>
#include <drm/ttm/ttm_module.h>
#include "vmwgfx_fence.h"
#include <linux/sync_file.h>

#define VMWGFX_DRIVER_DATE "20170607"
#define VMWGFX_DRIVER_NAME "vmwgfx"
#define VMWGFX_DRIVER_DATE "20170612"
#define VMWGFX_DRIVER_MAJOR 2
#define VMWGFX_DRIVER_MINOR 13
#define VMWGFX_DRIVER_MINOR 14
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
@@ -351,6 +353,12 @@ struct vmw_otable_batch {
	struct ttm_buffer_object *otable_bo;
};

enum {
	VMW_IRQTHREAD_FENCE,
	VMW_IRQTHREAD_CMDBUF,
	VMW_IRQTHREAD_MAX
};

struct vmw_private {
	struct ttm_bo_device bdev;
	struct ttm_bo_global_ref bo_global_ref;
@@ -529,6 +537,7 @@ struct vmw_private {
	struct vmw_otable_batch otable_batch;

	struct vmw_cmdbuf_man *cman;
	DECLARE_BITMAP(irqthread_pending, VMW_IRQTHREAD_MAX);
};

static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
@@ -561,24 +570,21 @@ static inline struct vmw_master *vmw_master(struct drm_master *master)
static inline void vmw_write(struct vmw_private *dev_priv,
			     unsigned int offset, uint32_t value)
{
	unsigned long irq_flags;

	spin_lock_irqsave(&dev_priv->hw_lock, irq_flags);
	spin_lock(&dev_priv->hw_lock);
	outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
	outl(value, dev_priv->io_start + VMWGFX_VALUE_PORT);
	spin_unlock_irqrestore(&dev_priv->hw_lock, irq_flags);
	spin_unlock(&dev_priv->hw_lock);
}

static inline uint32_t vmw_read(struct vmw_private *dev_priv,
				unsigned int offset)
{
	unsigned long irq_flags;
	u32 val;

	spin_lock_irqsave(&dev_priv->hw_lock, irq_flags);
	spin_lock(&dev_priv->hw_lock);
	outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
	val = inl(dev_priv->io_start + VMWGFX_VALUE_PORT);
	spin_unlock_irqrestore(&dev_priv->hw_lock, irq_flags);
	spin_unlock(&dev_priv->hw_lock);

	return val;
}
@@ -821,7 +827,8 @@ extern int vmw_execbuf_process(struct drm_file *file_priv,
			       uint32_t dx_context_handle,
			       struct drm_vmw_fence_rep __user
			       *user_fence_rep,
			       struct vmw_fence_obj **out_fence);
			       struct vmw_fence_obj **out_fence,
			       uint32_t flags);
extern void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
					    struct vmw_fence_obj *fence);
extern void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv);
@@ -836,23 +843,23 @@ extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
					struct drm_vmw_fence_rep __user
					*user_fence_rep,
					struct vmw_fence_obj *fence,
					uint32_t fence_handle);
					uint32_t fence_handle,
					int32_t out_fence_fd,
					struct sync_file *sync_file);
extern int vmw_validate_single_buffer(struct vmw_private *dev_priv,
				      struct ttm_buffer_object *bo,
				      bool interruptible,
				      bool validate_as_mob);

bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd);

/**
 * IRQs and wating - vmwgfx_irq.c
 */

extern irqreturn_t vmw_irq_handler(int irq, void *arg);
extern int vmw_wait_seqno(struct vmw_private *dev_priv, bool lazy,
			  uint32_t seqno, bool interruptible,
			  unsigned long timeout);
extern void vmw_irq_preinstall(struct drm_device *dev);
extern int vmw_irq_postinstall(struct drm_device *dev);
extern int vmw_irq_install(struct drm_device *dev, int irq);
extern void vmw_irq_uninstall(struct drm_device *dev);
extern bool vmw_seqno_passed(struct vmw_private *dev_priv,
				uint32_t seqno);
@@ -1150,13 +1157,13 @@ extern void *vmw_cmdbuf_reserve(struct vmw_cmdbuf_man *man, size_t size,
extern void vmw_cmdbuf_commit(struct vmw_cmdbuf_man *man, size_t size,
			      struct vmw_cmdbuf_header *header,
			      bool flush);
extern void vmw_cmdbuf_tasklet_schedule(struct vmw_cmdbuf_man *man);
extern void *vmw_cmdbuf_alloc(struct vmw_cmdbuf_man *man,
			      size_t size, bool interruptible,
			      struct vmw_cmdbuf_header **p_header);
extern void vmw_cmdbuf_header_free(struct vmw_cmdbuf_header *header);
extern int vmw_cmdbuf_cur_flush(struct vmw_cmdbuf_man *man,
				bool interruptible);
extern void vmw_cmdbuf_irqthread(struct vmw_cmdbuf_man *man);


/**
+133 −15
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/
#include <linux/sync_file.h>

#include "vmwgfx_drv.h"
#include "vmwgfx_reg.h"
@@ -112,11 +113,12 @@ struct vmw_cmd_entry {
	bool user_allow;
	bool gb_disable;
	bool gb_enable;
	const char *cmd_name;
};

#define VMW_CMD_DEF(_cmd, _func, _user_allow, _gb_disable, _gb_enable)	\
	[(_cmd) - SVGA_3D_CMD_BASE] = {(_func), (_user_allow),\
				       (_gb_disable), (_gb_enable)}
				       (_gb_disable), (_gb_enable), #_cmd}

static int vmw_resource_context_res_add(struct vmw_private *dev_priv,
					struct vmw_sw_context *sw_context,
@@ -3302,6 +3304,8 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = {
		    true, false, true),
	VMW_CMD_DEF(SVGA_3D_CMD_NOP, &vmw_cmd_ok,
		    true, false, true),
	VMW_CMD_DEF(SVGA_3D_CMD_NOP_ERROR, &vmw_cmd_ok,
		    true, false, true),
	VMW_CMD_DEF(SVGA_3D_CMD_ENABLE_GART, &vmw_cmd_invalid,
		    false, false, true),
	VMW_CMD_DEF(SVGA_3D_CMD_DISABLE_GART, &vmw_cmd_invalid,
@@ -3469,6 +3473,51 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = {
		    true, false, true),
};

bool vmw_cmd_describe(const void *buf, u32 *size, char const **cmd)
{
	u32 cmd_id = ((u32 *) buf)[0];

	if (cmd_id >= SVGA_CMD_MAX) {
		SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;
		const struct vmw_cmd_entry *entry;

		*size = header->size + sizeof(SVGA3dCmdHeader);
		cmd_id = header->id;
		if (cmd_id >= SVGA_3D_CMD_MAX)
			return false;

		cmd_id -= SVGA_3D_CMD_BASE;
		entry = &vmw_cmd_entries[cmd_id];
		*cmd = entry->cmd_name;
		return true;
	}

	switch (cmd_id) {
	case SVGA_CMD_UPDATE:
		*cmd = "SVGA_CMD_UPDATE";
		*size = sizeof(u32) + sizeof(SVGAFifoCmdUpdate);
		break;
	case SVGA_CMD_DEFINE_GMRFB:
		*cmd = "SVGA_CMD_DEFINE_GMRFB";
		*size = sizeof(u32) + sizeof(SVGAFifoCmdDefineGMRFB);
		break;
	case SVGA_CMD_BLIT_GMRFB_TO_SCREEN:
		*cmd = "SVGA_CMD_BLIT_GMRFB_TO_SCREEN";
		*size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen);
		break;
	case SVGA_CMD_BLIT_SCREEN_TO_GMRFB:
		*cmd = "SVGA_CMD_BLIT_SCREEN_TO_GMRFB";
		*size = sizeof(u32) + sizeof(SVGAFifoCmdBlitGMRFBToScreen);
		break;
	default:
		*cmd = "UNKNOWN";
		*size = 0;
		return false;
	}

	return true;
}

static int vmw_cmd_check(struct vmw_private *dev_priv,
			 struct vmw_sw_context *sw_context,
			 void *buf, uint32_t *size)
@@ -3781,6 +3830,8 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv,
 * which the information should be copied.
 * @fence: Pointer to the fenc object.
 * @fence_handle: User-space fence handle.
 * @out_fence_fd: exported file descriptor for the fence.  -1 if not used
 * @sync_file:  Only used to clean up in case of an error in this function.
 *
 * This function copies fence information to user-space. If copying fails,
 * The user-space struct drm_vmw_fence_rep::error member is hopefully
@@ -3796,7 +3847,9 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
			    int ret,
			    struct drm_vmw_fence_rep __user *user_fence_rep,
			    struct vmw_fence_obj *fence,
			    uint32_t fence_handle)
			    uint32_t fence_handle,
			    int32_t out_fence_fd,
			    struct sync_file *sync_file)
{
	struct drm_vmw_fence_rep fence_rep;

@@ -3806,6 +3859,7 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
	memset(&fence_rep, 0, sizeof(fence_rep));

	fence_rep.error = ret;
	fence_rep.fd = out_fence_fd;
	if (ret == 0) {
		BUG_ON(fence == NULL);

@@ -3828,6 +3882,14 @@ vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv,
	 * and unreference the handle.
	 */
	if (unlikely(ret != 0) && (fence_rep.error == 0)) {
		if (sync_file)
			fput(sync_file->file);

		if (fence_rep.fd != -1) {
			put_unused_fd(fence_rep.fd);
			fence_rep.fd = -1;
		}

		ttm_ref_object_base_unref(vmw_fp->tfile,
					  fence_handle, TTM_REF_USAGE);
		DRM_ERROR("Fence copy error. Syncing.\n");
@@ -4003,7 +4065,8 @@ int vmw_execbuf_process(struct drm_file *file_priv,
			uint64_t throttle_us,
			uint32_t dx_context_handle,
			struct drm_vmw_fence_rep __user *user_fence_rep,
			struct vmw_fence_obj **out_fence)
			struct vmw_fence_obj **out_fence,
			uint32_t flags)
{
	struct vmw_sw_context *sw_context = &dev_priv->ctx;
	struct vmw_fence_obj *fence = NULL;
@@ -4013,20 +4076,33 @@ int vmw_execbuf_process(struct drm_file *file_priv,
	struct ww_acquire_ctx ticket;
	uint32_t handle;
	int ret;
	int32_t out_fence_fd = -1;
	struct sync_file *sync_file = NULL;


	if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) {
		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
		if (out_fence_fd < 0) {
			DRM_ERROR("Failed to get a fence file descriptor.\n");
			return out_fence_fd;
		}
	}

	if (throttle_us) {
		ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue,
				   throttle_us);

		if (ret)
			return ret;
			goto out_free_fence_fd;
	}

	kernel_commands = vmw_execbuf_cmdbuf(dev_priv, user_commands,
					     kernel_commands, command_size,
					     &header);
	if (IS_ERR(kernel_commands))
		return PTR_ERR(kernel_commands);
	if (IS_ERR(kernel_commands)) {
		ret = PTR_ERR(kernel_commands);
		goto out_free_fence_fd;
	}

	ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
	if (ret) {
@@ -4162,8 +4238,32 @@ int vmw_execbuf_process(struct drm_file *file_priv,
		__vmw_execbuf_release_pinned_bo(dev_priv, fence);

	vmw_clear_validations(sw_context);

	/*
	 * If anything fails here, give up trying to export the fence
	 * and do a sync since the user mode will not be able to sync
	 * the fence itself.  This ensures we are still functionally
	 * correct.
	 */
	if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) {

		sync_file = sync_file_create(&fence->base);
		if (!sync_file) {
			DRM_ERROR("Unable to create sync file for fence\n");
			put_unused_fd(out_fence_fd);
			out_fence_fd = -1;

			(void) vmw_fence_obj_wait(fence, false, false,
						  VMW_FENCE_WAIT_TIMEOUT);
		} else {
			/* Link the fence with the FD created earlier */
			fd_install(out_fence_fd, sync_file->file);
		}
	}

	vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
				    user_fence_rep, fence, handle);
				    user_fence_rep, fence, handle,
				    out_fence_fd, sync_file);

	/* Don't unreference when handing fence out */
	if (unlikely(out_fence != NULL)) {
@@ -4214,6 +4314,9 @@ int vmw_execbuf_process(struct drm_file *file_priv,
out_free_header:
	if (header)
		vmw_cmdbuf_header_free(header);
out_free_fence_fd:
	if (out_fence_fd >= 0)
		put_unused_fd(out_fence_fd);

	return ret;
}
@@ -4366,6 +4469,7 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data,
	static const size_t copy_offset[] = {
		offsetof(struct drm_vmw_execbuf_arg, context_handle),
		sizeof(struct drm_vmw_execbuf_arg)};
	struct dma_fence *in_fence = NULL;

	if (unlikely(size < copy_offset[0])) {
		DRM_ERROR("Invalid command size, ioctl %d\n",
@@ -4401,15 +4505,25 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data,
		arg.context_handle = (uint32_t) -1;
		break;
	case 2:
		if (arg.pad64 != 0) {
			DRM_ERROR("Unused IOCTL data not set to zero.\n");
			return -EINVAL;
		}
		break;
	default:
		break;
	}


	/* If imported a fence FD from elsewhere, then wait on it */
	if (arg.flags & DRM_VMW_EXECBUF_FLAG_IMPORT_FENCE_FD) {
		in_fence = sync_file_get_fence(arg.imported_fence_fd);

		if (!in_fence) {
			DRM_ERROR("Cannot get imported fence\n");
			return -EINVAL;
		}

		ret = vmw_wait_dma_fence(dev_priv->fman, in_fence);
		if (ret)
			goto out;
	}

	ret = ttm_read_lock(&dev_priv->reservation_sem, true);
	if (unlikely(ret != 0))
		return ret;
@@ -4419,12 +4533,16 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data,
				  NULL, arg.command_size, arg.throttle_us,
				  arg.context_handle,
				  (void __user *)(unsigned long)arg.fence_rep,
				  NULL);
				  NULL,
				  arg.flags);
	ttm_read_unlock(&dev_priv->reservation_sem);
	if (unlikely(ret != 0))
		return ret;
		goto out;

	vmw_kms_cursor_post_execbuf(dev_priv);

	return 0;
out:
	if (in_fence)
		dma_fence_put(in_fence);
	return ret;
}
+70 −34

File changed.

Preview size limit exceeded, changes collapsed.

Loading