diff options
author | Jakob Bornecrantz <jakob@vmware.com> | 2009-12-14 21:36:21 +0100 |
---|---|---|
committer | Jakob Bornecrantz <jakob@vmware.com> | 2009-12-14 21:36:48 +0100 |
commit | bb9b570e01fa263d3d20a59fb8715df566233f38 (patch) | |
tree | 208fe3fde4debd7980c30d81c97b3f7a326d9d09 | |
parent | da88394c0195bd3e7102e99cea1f3da72938856d (diff) |
vmwgfx: Userspace must now claim a overlay before use
-rw-r--r-- | vmwgfx_drm.h | 29 | ||||
-rw-r--r-- | vmwgfx_drv.c | 15 | ||||
-rw-r--r-- | vmwgfx_drv.h | 11 | ||||
-rw-r--r-- | vmwgfx_overlay.c | 55 | ||||
-rw-r--r-- | vmwgfx_resource.c | 185 |
5 files changed, 291 insertions, 4 deletions
diff --git a/vmwgfx_drm.h b/vmwgfx_drm.h index e05731b..46f6a31 100644 --- a/vmwgfx_drm.h +++ b/vmwgfx_drm.h @@ -46,6 +46,8 @@ #define DRM_VMW_FENCE_WAIT 12 #define DRM_VMW_OVERLAY 13 #define DRM_VMW_CURSOR_BYPASS 14 +#define DRM_VMW_CLAIM_STREAM 15 +#define DRM_VMW_UNREF_STREAM 16 /*************************************************************************/ /** @@ -537,4 +539,31 @@ struct drm_vmw_cursor_bypass_arg { int32_t yhot; }; +/*************************************************************************/ +/** + * DRM_VMW_CLAIM_STREAM - Claim a single stream. + */ + +/** + * struct drm_vmw_context_arg + * + * @stream_id: Device unique context ID. + * + * Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl. + * Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl. + */ + +struct drm_vmw_stream_arg { + uint32_t stream_id; + uint32_t pad64; +}; + +/*************************************************************************/ +/** + * DRM_VMW_UNREF_STREAM - Unclaim a stream. + * + * Return a single stream that was claimed by this process. Also makes + * sure that the stream has been stopped. + */ + #endif diff --git a/vmwgfx_drv.c b/vmwgfx_drv.c index 749cf38..0c756bf 100644 --- a/vmwgfx_drv.c +++ b/vmwgfx_drv.c @@ -80,6 +80,12 @@ #define DRM_IOCTL_VMW_CURSOR_BYPASS \ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CURSOR_BYPASS, \ struct drm_vmw_cursor_bypass_arg) +#define DRM_IOCTL_VMW_CLAIM_STREAM \ + DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CLAIM_STREAM, \ + struct drm_vmw_stream_arg) +#define DRM_IOCTL_VMW_UNREF_STREAM \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_STREAM, \ + struct drm_vmw_stream_arg) /** * The core DRM version of this macro doesn't account for @@ -118,7 +124,11 @@ static struct drm_ioctl_desc vmw_ioctls[] = { VMW_IOCTL_DEF(DRM_IOCTL_VMW_OVERLAY, vmw_overlay_ioctl, 0), VMW_IOCTL_DEF(DRM_IOCTL_VMW_CURSOR_BYPASS, - vmw_kms_cursor_bypass_ioctl, 0) + vmw_kms_cursor_bypass_ioctl, 0), + VMW_IOCTL_DEF(DRM_IOCTL_VMW_CLAIM_STREAM, vmw_stream_claim_ioctl, + 0), + VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_STREAM, vmw_stream_unref_ioctl, + 0) }; static struct pci_device_id vmw_pci_id_list[] = { @@ -205,6 +215,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) rwlock_init(&dev_priv->resource_lock); idr_init(&dev_priv->context_idr); idr_init(&dev_priv->surface_idr); + idr_init(&dev_priv->stream_idr); ida_init(&dev_priv->gmr_ida); mutex_init(&dev_priv->init_mutex); init_waitqueue_head(&dev_priv->fence_queue); @@ -363,6 +374,7 @@ out_err0: ida_destroy(&dev_priv->gmr_ida); idr_destroy(&dev_priv->surface_idr); idr_destroy(&dev_priv->context_idr); + idr_destroy(&dev_priv->stream_idr); kfree(dev_priv); return ret; } @@ -398,6 +410,7 @@ static int vmw_driver_unload(struct drm_device *dev) ida_destroy(&dev_priv->gmr_ida); idr_destroy(&dev_priv->surface_idr); idr_destroy(&dev_priv->context_idr); + idr_destroy(&dev_priv->stream_idr); kfree(dev_priv); diff --git a/vmwgfx_drv.h b/vmwgfx_drv.h index 20f2118..884c99f 100644 --- a/vmwgfx_drv.h +++ b/vmwgfx_drv.h @@ -195,6 +195,7 @@ struct vmw_private { rwlock_t resource_lock; struct idr context_idr; struct idr surface_idr; + struct idr stream_idr; /* * Block lastclose from racing with firstopen. @@ -355,6 +356,14 @@ extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, struct vmw_dma_buffer *bo); extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv, struct vmw_dma_buffer *bo); +extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, + struct vmw_resource **out); /** @@ -461,6 +470,8 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data, int vmw_overlay_stop_all(struct vmw_private *dev_priv); int vmw_overlay_resume_all(struct vmw_private *dev_priv); int vmw_overlay_pause_all(struct vmw_private *dev_priv); +int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out); +int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id); /** * Inline helper functions diff --git a/vmwgfx_overlay.c b/vmwgfx_overlay.c index 5b146a4..65e6045 100644 --- a/vmwgfx_overlay.c +++ b/vmwgfx_overlay.c @@ -38,6 +38,7 @@ struct vmw_stream { struct vmw_dma_buffer *buf; + bool claimed; bool paused; struct drm_vmw_overlay_arg saved; }; @@ -476,14 +477,16 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data, struct drm_vmw_overlay_arg *arg = (struct drm_vmw_overlay_arg *)data; struct vmw_dma_buffer *buf; + struct vmw_resource *res; int ret; - if (arg->stream_id > VMW_MAX_NUM_STREAMS) - return -EINVAL; - if (!overlay) return -ENOSYS; + ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res); + if (ret) + return ret; + mutex_lock(&overlay->mutex); if (!arg->enabled) { @@ -501,10 +504,55 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data, out_unlock: mutex_unlock(&overlay->mutex); + vmw_resource_unreference(&res); return ret; } +int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + int i; + + if (!overlay) + return -ENOSYS; + + mutex_lock(&overlay->mutex); + + for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { + + if (overlay->stream[i].claimed) + continue; + + overlay->stream[i].claimed = true; + *out = i; + mutex_unlock(&overlay->mutex); + return 0; + } + + mutex_unlock(&overlay->mutex); + return -ESRCH; +} + +int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id) +{ + struct vmw_overlay *overlay = dev_priv->overlay_priv; + + BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS); + + if (!overlay) + return -ENOSYS; + + mutex_lock(&overlay->mutex); + + WARN_ON(!overlay->stream[stream_id].claimed); + vmw_overlay_stop(dev_priv, stream_id, false, false); + overlay->stream[stream_id].claimed = false; + + mutex_unlock(&overlay->mutex); + return 0; +} + int vmw_overlay_init(struct vmw_private *dev_priv) { struct vmw_overlay *overlay; @@ -528,6 +576,7 @@ int vmw_overlay_init(struct vmw_private *dev_priv) for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) { overlay->stream[i].buf = NULL; overlay->stream[i].paused = false; + overlay->stream[i].claimed = false; } dev_priv->overlay_priv = overlay; diff --git a/vmwgfx_resource.c b/vmwgfx_resource.c index 6538c68..038e524 100644 --- a/vmwgfx_resource.c +++ b/vmwgfx_resource.c @@ -33,6 +33,7 @@ #define VMW_RES_CONTEXT ttm_driver_type0 #define VMW_RES_SURFACE ttm_driver_type1 +#define VMW_RES_STREAM ttm_driver_type2 struct vmw_user_context { struct ttm_base_object base; @@ -54,6 +55,16 @@ struct vmw_bo_user_rep { uint64_t map_handle; }; +struct vmw_stream { + struct vmw_resource res; + uint32_t stream_id; +}; + +struct vmw_user_stream { + struct ttm_base_object base; + struct vmw_stream stream; +}; + static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -1005,3 +1016,177 @@ int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id) *p_id = (uint32_t) id; return 0; } + +/* + * Stream managment + */ + +static void vmw_stream_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_stream *stream; + int ret; + + DRM_INFO("%s: unref\n", __func__); + stream = container_of(res, struct vmw_stream, res); + + ret = vmw_overlay_unref(dev_priv, stream->stream_id); + WARN_ON(ret != 0); +} + +static int vmw_stream_init(struct vmw_private *dev_priv, + struct vmw_stream *stream, + void (*res_free) (struct vmw_resource *res)) +{ + struct vmw_resource *res = &stream->res; + int ret; + + ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, + VMW_RES_STREAM, res_free); + + if (unlikely(ret != 0)) { + if (res_free == NULL) + kfree(stream); + else + res_free(&stream->res); + return ret; + } + + ret = vmw_overlay_claim(dev_priv, &stream->stream_id); + if (ret) { + vmw_resource_unreference(&res); + return ret; + } + + DRM_INFO("%s: claimed\n", __func__); + + vmw_resource_activate(&stream->res, vmw_stream_destroy); + return 0; +} + +/** + * User-space context management: + */ + +static void vmw_user_stream_free(struct vmw_resource *res) +{ + struct vmw_user_stream *stream = + container_of(res, struct vmw_user_stream, stream.res); + + kfree(stream); +} + +/** + * This function is called when user space has no more references on the + * base object. It releases the base-object's reference on the resource object. + */ + +static void vmw_user_stream_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_stream *stream = + container_of(base, struct vmw_user_stream, base); + struct vmw_resource *res = &stream->stream.res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_resource *res; + struct vmw_user_stream *stream; + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + int ret = 0; + + res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id); + if (unlikely(res == NULL)) + return -EINVAL; + + if (res->res_free != &vmw_user_stream_free) { + ret = -EINVAL; + goto out; + } + + stream = container_of(res, struct vmw_user_stream, stream.res); + if (stream->base.tfile != tfile) { + ret = -EINVAL; + goto out; + } + + ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE); +out: + vmw_resource_unreference(&res); + return ret; +} + +int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); + struct vmw_resource *res; + struct vmw_resource *tmp; + struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + int ret; + + if (unlikely(stream == NULL)) + return -ENOMEM; + + res = &stream->stream.res; + stream->base.shareable = false; + stream->base.tfile = NULL; + + ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); + if (unlikely(ret != 0)) + return ret; + + tmp = vmw_resource_reference(res); + ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, + &vmw_user_stream_base_release, NULL); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + arg->stream_id = res->id; +out_err: + vmw_resource_unreference(&res); + return ret; +} + +int vmw_user_stream_lookup(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t *inout_id, struct vmw_resource **out) +{ + struct vmw_user_stream *stream; + struct vmw_resource *res; + int ret; + + res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id); + if (unlikely(res == NULL)) + return -EINVAL; + + if (res->res_free != &vmw_user_stream_free) { + ret = -EINVAL; + goto err_ref; + } + + stream = container_of(res, struct vmw_user_stream, stream.res); + if (stream->base.tfile != tfile) { + ret = -EPERM; + goto err_ref; + } + + *inout_id = stream->stream.stream_id; + *out = res; + return 0; +err_ref: + vmw_resource_unreference(&res); + return ret; +} |