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

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
......@@ -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;
......@@ -181,12 +184,13 @@ struct vmw_cmdbuf_alloc_info {
};
/* Loop over each context in the command buffer manager. */
#define for_each_cmdbuf_ctx(_man, _i, _ctx) \
#define for_each_cmdbuf_ctx(_man, _i, _ctx) \
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);
__vmw_cmdbuf_header_free(entry);
wake_up_all(&man->idle_queue);
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);
send_fence = true;
continue;
}
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;
}
spin_unlock_bh(&man->lock);
if (restart && vmw_cmdbuf_startstop(man, true))
DRM_ERROR("Failed restarting command buffer context 0.\n");
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 */
vmw_fifo_send_fence(man->dev_priv, &dummy);
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,11 +1341,14 @@ 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);
if (ret) {
DRM_ERROR("Failed starting command buffer context 0.\n");
vmw_cmdbuf_man_destroy(man);
return ERR_PTR(ret);
for_each_cmdbuf_ctx(man, i, ctx) {
ret = vmw_cmdbuf_startstop(man, i, true);
if (ret) {
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);
}
......@@ -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,
......
......@@ -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);
/**
......
......@@ -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;
}
......@@ -114,12 +114,11 @@ static void vmw_fence_obj_destroy(struct dma_fence *f)
container_of(f, struct vmw_fence_obj, base);
struct vmw_fence_manager *fman = fman_from_fence(fence);
unsigned long irq_flags;
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
list_del_init(&fence->head);
--fman->num_fence_objects;
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
fence->destroy(fence);
}
......@@ -252,10 +251,10 @@ static void vmw_fence_work_func(struct work_struct *work)
INIT_LIST_HEAD(&list);
mutex_lock(&fman->goal_irq_mutex);
spin_lock_irq(&fman->lock);
spin_lock(&fman->lock);
list_splice_init(&fman->cleanup_list, &list);
seqno_valid = fman->seqno_valid;
spin_unlock_irq(&fman->lock);
spin_unlock(&fman->lock);
if (!seqno_valid && fman->goal_irq_on) {
fman->goal_irq_on = false;
......@@ -305,15 +304,14 @@ struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv)
void vmw_fence_manager_takedown(struct vmw_fence_manager *fman)
{
unsigned long irq_flags;
bool lists_empty;
(void) cancel_work_sync(&fman->work);
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
lists_empty = list_empty(&fman->fence_list) &&
list_empty(&fman->cleanup_list);
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
BUG_ON(!lists_empty);
kfree(fman);
......@@ -323,7 +321,6 @@ static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
struct vmw_fence_obj *fence, u32 seqno,
void (*destroy) (struct vmw_fence_obj *fence))
{
unsigned long irq_flags;
int ret = 0;
dma_fence_init(&fence->base, &vmw_fence_ops, &fman->lock,
......@@ -331,7 +328,7 @@ static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
INIT_LIST_HEAD(&fence->seq_passed_actions);
fence->destroy = destroy;
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
if (unlikely(fman->fifo_down)) {
ret = -EBUSY;
goto out_unlock;
......@@ -340,7 +337,7 @@ static int vmw_fence_obj_init(struct vmw_fence_manager *fman,
++fman->num_fence_objects;
out_unlock:
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
return ret;
}
......@@ -489,11 +486,9 @@ static void __vmw_fences_update(struct vmw_fence_manager *fman)
void vmw_fences_update(struct vmw_fence_manager *fman)
{
unsigned long irq_flags;
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
__vmw_fences_update(fman);
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
}
bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence)
......@@ -649,6 +644,51 @@ int vmw_user_fence_create(struct drm_file *file_priv,
}
/**
* vmw_wait_dma_fence - Wait for a dma fence
*
* @fman: pointer to a fence manager
* @fence: DMA fence to wait on
*
* This function handles the case when the fence is actually a fence
* array. If that's the case, it'll wait on each of the child fence
*/
int vmw_wait_dma_fence(struct vmw_fence_manager *fman,
struct dma_fence *fence)
{
struct dma_fence_array *fence_array;
int ret = 0;
int i;
if (dma_fence_is_signaled(fence))
return 0;
if (!dma_fence_is_array(fence))
return dma_fence_wait(fence, true);
/* From i915: Note that if the fence-array was created in
* signal-on-any mode, we should *not* decompose it into its individual
* fences. However, we don't currently store which mode the fence-array
* is operating in. Fortunately, the only user of signal-on-any is
* private to amdgpu and we should not see any incoming fence-array
* from sync-file being in signal-on-any mode.
*/
fence_array = to_dma_fence_array(fence);
for (i = 0; i < fence_array->num_fences; i++) {
struct dma_fence *child = fence_array->fences[i];
ret = dma_fence_wait(child, true);
if (ret < 0)
return ret;
}
return 0;
}
/**
* vmw_fence_fifo_down - signal all unsignaled fence objects.
*/
......@@ -663,14 +703,14 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
* restart when we've released the fman->lock.
*/
spin_lock_irq(&fman->lock);
spin_lock(&fman->lock);
fman->fifo_down = true;
while (!list_empty(&fman->fence_list)) {
struct vmw_fence_obj *fence =
list_entry(fman->fence_list.prev, struct vmw_fence_obj,
head);
dma_fence_get(&fence->base);
spin_unlock_irq(&fman->lock);
spin_unlock(&fman->lock);
ret = vmw_fence_obj_wait(fence, false, false,
VMW_FENCE_WAIT_TIMEOUT);
......@@ -686,18 +726,16 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
BUG_ON(!list_empty(&fence->head));
dma_fence_put(&fence->base);
spin_lock_irq(&fman->lock);
spin_lock(&fman->lock);
}
spin_unlock_irq(&fman->lock);
spin_unlock(&fman->lock);
}
void vmw_fence_fifo_up(struct vmw_fence_manager *fman)
{
unsigned long irq_flags;
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
fman->fifo_down = false;
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
}
......@@ -812,9 +850,9 @@ int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data,
arg->signaled = vmw_fence_obj_signaled(fence);
arg->signaled_flags = arg->flags;
spin_lock_irq(&fman->lock);
spin_lock(&fman->lock);
arg->passed_seqno = dev_priv->last_read_seqno;
spin_unlock_irq(&fman->lock);
spin_unlock(&fman->lock);
ttm_base_object_unref(&base);
......@@ -841,8 +879,7 @@ int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
*
* This function is called when the seqno of the fence where @action is
* attached has passed. It queues the event on the submitter's event list.
* This function is always called from atomic context, and may be called
* from irq context.
* This function is always called from atomic context.
*/
static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
{
......@@ -851,13 +888,13 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
struct drm_device *dev = eaction->dev;
struct drm_pending_event *event = eaction->event;
struct drm_file *file_priv;
unsigned long irq_flags;
if (unlikely(event == NULL))
return;
file_priv = event->file_priv;
spin_lock_irqsave(&dev->event_lock, irq_flags);
spin_lock_irq(&dev->event_lock);
if (likely(eaction->tv_sec != NULL)) {
struct timeval tv;
......@@ -869,7 +906,7 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
drm_send_event_locked(dev, eaction->event);
eaction->event = NULL;
spin_unlock_irqrestore(&dev->event_lock, irq_flags);
spin_unlock_irq(&dev->event_lock);
}
/**
......@@ -904,11 +941,10 @@ static void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
struct vmw_fence_action *action)
{
struct vmw_fence_manager *fman = fman_from_fence(fence);
unsigned long irq_flags;
bool run_update = false;
mutex_lock(&fman->goal_irq_mutex);
spin_lock_irqsave(&fman->lock, irq_flags);
spin_lock(&fman->lock);
fman->pending_actions[action->type]++;
if (dma_fence_is_signaled_locked(&fence->base)) {
......@@ -927,7 +963,7 @@ static void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
run_update = vmw_fence_goal_check_locked(fence);
}
spin_unlock_irqrestore(&fman->lock, irq_flags);
spin_unlock(&fman->lock);
if (run_update) {
if (!fman->goal_irq_on) {
......@@ -1114,7 +1150,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
}
vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
handle);
handle, -1, NULL);
vmw_fence_obj_unreference(&fence);
return 0;
out_no_create:
......
......@@ -28,6 +28,7 @@
#ifndef _VMWGFX_FENCE_H_
#include <linux/dma-fence.h>
#include <linux/dma-fence-array.h>
#define VMW_FENCE_WAIT_TIMEOUT (5*HZ)
......@@ -102,6 +103,9 @@ extern int vmw_user_fence_create(struct drm_file *file_priv,
struct vmw_fence_obj **p_fence,
uint32_t *p_handle);
extern int vmw_wait_dma_fence(struct vmw_fence_manager *fman,
struct dma_fence *fence);
extern void vmw_fence_fifo_up(struct vmw_fence_manager *fman);
extern void vmw_fence_fifo_down(struct vmw_fence_manager *fman);
......
......@@ -30,11 +30,56 @@
#define VMW_FENCE_WRAP (1 << 24)
irqreturn_t vmw_irq_handler(int irq, void *arg)
/**
* vmw_thread_fn - Deferred (process context) irq handler
*
* @irq: irq number
* @arg: Closure argument. Pointer to a struct drm_device cast to void *
*
* This function implements the deferred part of irq processing.
* The function is guaranteed to run at least once after the
* vmw_irq_handler has returned with IRQ_WAKE_THREAD.
*
*/
static irqreturn_t vmw_thread_fn(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *)arg;
struct vmw_private *dev_priv = vmw_priv(dev);
irqreturn_t ret = IRQ_NONE;
if (test_and_clear_bit(VMW_IRQTHREAD_FENCE,
dev_priv->irqthread_pending)) {
vmw_fences_update(dev_priv->fman);
wake_up_all(&dev_priv->fence_queue);
ret = IRQ_HANDLED;
}
if (test_and_clear_bit(VMW_IRQTHREAD_CMDBUF,
dev_priv->irqthread_pending)) {
vmw_cmdbuf_irqthread(dev_priv->cman);
ret = IRQ_HANDLED;
}
return ret;
}
/**
* vmw_irq_handler irq handler
*
* @irq: irq number
* @arg: Closure argument. Pointer to a struct drm_device cast to void *
*
* This function implements the quick part of irq processing.
* The function performs fast actions like clearing the device interrupt
* flags and also reasonably quick actions like waking processes waiting for
* FIFO space. Other IRQ actions are deferred to the IRQ thread.
*/
static irqreturn_t vmw_irq_handler(int irq, void *arg)
{
struct drm_device *dev = (struct drm_device *)arg;
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status, masked_status;
irqreturn_t ret = IRQ_HANDLED;
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
masked_status = status & READ_ONCE(dev_priv->irq_mask);
......@@ -45,20 +90,21 @@ irqreturn_t vmw_irq_handler(int irq, void *arg)
if (!status)
return IRQ_NONE;
if (masked_status & (SVGA_IRQFLAG_ANY_FENCE |
SVGA_IRQFLAG_FENCE_GOAL)) {
vmw_fences_update(dev_priv->fman);
wake_up_all(&dev_priv->fence_queue);
}
if (masked_status & SVGA_IRQFLAG_FIFO_PROGRESS)
wake_up_all(&dev_priv->fifo_queue);
if (masked_status & (SVGA_IRQFLAG_COMMAND_BUFFER |
SVGA_IRQFLAG_ERROR))
vmw_cmdbuf_tasklet_schedule(dev_priv->cman);
if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE |
SVGA_IRQFLAG_FENCE_GOAL)) &&
!test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending))
ret = IRQ_WAKE_THREAD;
return IRQ_HANDLED;
if ((masked_status & (SVGA_IRQFLAG_COMMAND_BUFFER |
SVGA_IRQFLAG_ERROR)) &&
!test_and_set_bit(VMW_IRQTHREAD_CMDBUF,
dev_priv->irqthread_pending))
ret = IRQ_WAKE_THREAD;
return ret;
}
static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno)
......@@ -281,23 +327,15 @@ int vmw_wait_seqno(struct vmw_private *dev_priv,
return ret;
}
void vmw_irq_preinstall(struct drm_device *dev)
static void vmw_irq_preinstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t status;
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
}
int vmw_irq_postinstall(struct drm_device *dev)
{
return 0;
}
void vmw_irq_uninstall(struct drm_device *dev)
{
struct vmw_private *dev_priv = vmw_priv(dev);
......@@ -306,8 +344,41 @@ void vmw_irq_uninstall(struct drm_device *dev)
if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
return;
if (!dev->irq_enabled)
return;
vmw_write(dev_priv, SVGA_REG_IRQMASK, 0);
status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
dev->irq_enabled = false;
free_irq(dev->irq, dev);
}
/**
* vmw_irq_install - Install the irq handlers
*
* @dev: Pointer to the drm device.
* @irq: The irq number.
* Return: Zero if successful. Negative number otherwise.
*/
int vmw_irq_install(struct drm_device *dev, int irq)
{
int ret;
if (dev->irq_enabled)
return -EBUSY;
vmw_irq_preinstall(dev);
ret = request_threaded_irq(irq, vmw_irq_handler, vmw_thread_fn,
IRQF_SHARED, VMWGFX_DRIVER_NAME, dev);
if (ret < 0)
return ret;
dev->irq_enabled = true;
dev->irq = irq;
return ret;
}
......@@ -2494,7 +2494,7 @@ void vmw_kms_helper_buffer_finish(struct vmw_private *dev_priv,
if (file_priv)
vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv),
ret, user_fence_rep, fence,
handle);
handle, -1, NULL);
if (out_fence)
*out_fence = fence;
else
......
......@@ -297,13 +297,17 @@ union drm_vmw_surface_reference_arg {
* @version: Allows expanding the execbuf ioctl parameters without breaking
* backwards compatibility, since user-space will always tell the kernel
* which version it uses.
* @flags: Execbuf flags. None currently.
* @flags: Execbuf flags.
* @imported_fence_fd: FD for a fence imported from another device
*
* Argument to the DRM_VMW_EXECBUF Ioctl.
*/
#define DRM_VMW_EXECBUF_VERSION 2
#define DRM_VMW_EXECBUF_FLAG_IMPORT_FENCE_FD (1 << 0)
#define DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD (1 << 1)
struct drm_vmw_execbuf_arg {
__u64 commands;
__u32 command_size;
......@@ -312,7 +316,7 @@ struct drm_vmw_execbuf_arg {
__u32 version;
__u32 flags;
__u32 context_handle;
__u32 pad64;
__s32 imported_fence_fd;
};
/**
......@@ -328,6 +332,7 @@ struct drm_vmw_execbuf_arg {
* @passed_seqno: The highest seqno number processed by the hardware
* so far. This can be used to mark user-space fence objects as signaled, and
* to determine whether a fence seqno might be stale.
* @fd: FD associated with the fence, -1 if not exported
* @error: This member should've been set to -EFAULT on submission.
* The following actions should be take on completion:
* error == -EFAULT: Fence communication failed. The host is synchronized.
......@@ -345,7 +350,7 @@ struct drm_vmw_fence_rep {
__u32 mask;
__u32 seqno;
__u32 passed_seqno;
__u32 pad64;
__s32 fd;
__s32 error;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment