提交 414ee50b 编写于 作者: T Thomas Hellstrom 提交者: Dave Airlie

vmwgfx: Implement memory accounting for resources

Contexts, surfaces and streams allocate persistent kernel memory as the
direct result of user-space requests. Make sure this memory is
accounted as graphics memory, to avoid DOS vulnerabilities.

Also take the TTM read lock around resource creation to block
switched-out dri clients from allocating resources.
Signed-off-by: NThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: NJakob Bornecrantz <jakob@vmware.com>
Signed-off-by: NDave Airlie <airlied@redhat.com>
上级 1c248b7d
...@@ -39,6 +39,7 @@ struct vmw_user_context { ...@@ -39,6 +39,7 @@ struct vmw_user_context {
struct vmw_user_surface { struct vmw_user_surface {
struct ttm_base_object base; struct ttm_base_object base;
struct vmw_surface srf; struct vmw_surface srf;
uint32_t size;
}; };
struct vmw_user_dma_buffer { struct vmw_user_dma_buffer {
...@@ -67,6 +68,11 @@ struct vmw_surface_offset { ...@@ -67,6 +68,11 @@ struct vmw_surface_offset {
uint32_t bo_offset; uint32_t bo_offset;
}; };
static uint64_t vmw_user_context_size;
static uint64_t vmw_user_surface_size;
static uint64_t vmw_user_stream_size;
static inline struct vmw_dma_buffer * static inline struct vmw_dma_buffer *
vmw_dma_buffer(struct ttm_buffer_object *bo) vmw_dma_buffer(struct ttm_buffer_object *bo)
{ {
...@@ -343,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res) ...@@ -343,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res)
{ {
struct vmw_user_context *ctx = struct vmw_user_context *ctx =
container_of(res, struct vmw_user_context, res); container_of(res, struct vmw_user_context, res);
struct vmw_private *dev_priv = res->dev_priv;
kfree(ctx); kfree(ctx);
ttm_mem_global_free(vmw_mem_glob(dev_priv),
vmw_user_context_size);
} }
/** /**
...@@ -398,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, ...@@ -398,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
struct vmw_private *dev_priv = vmw_priv(dev); struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); struct vmw_user_context *ctx;
struct vmw_resource *res; struct vmw_resource *res;
struct vmw_resource *tmp; struct vmw_resource *tmp;
struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret; int ret;
if (unlikely(ctx == NULL))
return -ENOMEM; /*
* Approximate idr memory usage with 128 bytes. It will be limited
* by maximum number_of contexts anyway.
*/
if (unlikely(vmw_user_context_size == 0))
vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
return ret;
ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
vmw_user_context_size,
false, true);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Out of graphics memory for context"
" creation.\n");
goto out_unlock;
}
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (unlikely(ctx == NULL)) {
ttm_mem_global_free(vmw_mem_glob(dev_priv),
vmw_user_context_size);
ret = -ENOMEM;
goto out_unlock;
}
res = &ctx->res; res = &ctx->res;
ctx->base.shareable = false; ctx->base.shareable = false;
ctx->base.tfile = NULL; ctx->base.tfile = NULL;
/*
* From here on, the destructor takes over resource freeing.
*/
ret = vmw_context_init(dev_priv, res, vmw_user_context_free); ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
if (unlikely(ret != 0)) if (unlikely(ret != 0))
return ret; goto out_unlock;
tmp = vmw_resource_reference(&ctx->res); tmp = vmw_resource_reference(&ctx->res);
ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
...@@ -428,6 +470,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, ...@@ -428,6 +470,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data,
arg->cid = res->id; arg->cid = res->id;
out_err: out_err:
vmw_resource_unreference(&res); vmw_resource_unreference(&res);
out_unlock:
ttm_read_unlock(&vmaster->lock);
return ret; return ret;
} }
...@@ -1095,6 +1139,8 @@ static void vmw_user_surface_free(struct vmw_resource *res) ...@@ -1095,6 +1139,8 @@ static void vmw_user_surface_free(struct vmw_resource *res)
struct vmw_surface *srf = container_of(res, struct vmw_surface, res); struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
struct vmw_user_surface *user_srf = struct vmw_user_surface *user_srf =
container_of(srf, struct vmw_user_surface, srf); container_of(srf, struct vmw_user_surface, srf);
struct vmw_private *dev_priv = srf->res.dev_priv;
uint32_t size = user_srf->size;
if (srf->backup) if (srf->backup)
ttm_bo_unref(&srf->backup); ttm_bo_unref(&srf->backup);
...@@ -1102,6 +1148,7 @@ static void vmw_user_surface_free(struct vmw_resource *res) ...@@ -1102,6 +1148,7 @@ static void vmw_user_surface_free(struct vmw_resource *res)
kfree(srf->sizes); kfree(srf->sizes);
kfree(srf->snooper.image); kfree(srf->snooper.image);
kfree(user_srf); kfree(user_srf);
ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
} }
/** /**
...@@ -1226,9 +1273,45 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1226,9 +1273,45 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
struct vmw_surface_offset *cur_offset; struct vmw_surface_offset *cur_offset;
uint32_t stride_bpp; uint32_t stride_bpp;
uint32_t bpp; uint32_t bpp;
uint32_t num_sizes;
uint32_t size;
struct vmw_master *vmaster = vmw_master(file_priv->master);
if (unlikely(user_srf == NULL)) if (unlikely(vmw_user_surface_size == 0))
return -ENOMEM; vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) +
128;
num_sizes = 0;
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
num_sizes += req->mip_levels[i];
if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
DRM_VMW_MAX_MIP_LEVELS)
return -EINVAL;
size = vmw_user_surface_size + 128 +
ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) +
ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset));
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
return ret;
ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
size, false, true);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Out of graphics memory for surface"
" creation.\n");
goto out_unlock;
}
user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL);
if (unlikely(user_srf == NULL)) {
ret = -ENOMEM;
goto out_no_user_srf;
}
srf = &user_srf->srf; srf = &user_srf->srf;
res = &srf->res; res = &srf->res;
...@@ -1239,20 +1322,13 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1239,20 +1322,13 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
srf->backup = NULL; srf->backup = NULL;
memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
srf->num_sizes = 0; srf->num_sizes = num_sizes;
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) user_srf->size = size;
srf->num_sizes += srf->mip_levels[i];
if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES *
DRM_VMW_MAX_MIP_LEVELS) {
ret = -EINVAL;
goto out_err0;
}
srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL);
if (unlikely(srf->sizes == NULL)) { if (unlikely(srf->sizes == NULL)) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_err0; goto out_no_sizes;
} }
srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets),
GFP_KERNEL); GFP_KERNEL);
...@@ -1268,7 +1344,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1268,7 +1344,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
srf->num_sizes * sizeof(*srf->sizes)); srf->num_sizes * sizeof(*srf->sizes));
if (unlikely(ret != 0)) { if (unlikely(ret != 0)) {
ret = -EFAULT; ret = -EFAULT;
goto out_err1; goto out_no_copy;
} }
cur_bo_offset = 0; cur_bo_offset = 0;
...@@ -1305,7 +1381,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1305,7 +1381,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
if (!srf->snooper.image) { if (!srf->snooper.image) {
DRM_ERROR("Failed to allocate cursor_image\n"); DRM_ERROR("Failed to allocate cursor_image\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out_err1; goto out_no_copy;
} }
} else { } else {
srf->snooper.image = NULL; srf->snooper.image = NULL;
...@@ -1322,7 +1398,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1322,7 +1398,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free);
if (unlikely(ret != 0)) if (unlikely(ret != 0))
return ret; goto out_unlock;
tmp = vmw_resource_reference(&srf->res); tmp = vmw_resource_reference(&srf->res);
ret = ttm_base_object_init(tfile, &user_srf->base, ret = ttm_base_object_init(tfile, &user_srf->base,
...@@ -1332,7 +1408,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1332,7 +1408,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0)) { if (unlikely(ret != 0)) {
vmw_resource_unreference(&tmp); vmw_resource_unreference(&tmp);
vmw_resource_unreference(&res); vmw_resource_unreference(&res);
return ret; goto out_unlock;
} }
rep->sid = user_srf->base.hash.key; rep->sid = user_srf->base.hash.key;
...@@ -1340,13 +1416,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ...@@ -1340,13 +1416,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
DRM_ERROR("Created bad Surface ID.\n"); DRM_ERROR("Created bad Surface ID.\n");
vmw_resource_unreference(&res); vmw_resource_unreference(&res);
ttm_read_unlock(&vmaster->lock);
return 0; return 0;
out_err1: out_no_copy:
kfree(srf->offsets); kfree(srf->offsets);
out_no_offsets: out_no_offsets:
kfree(srf->sizes); kfree(srf->sizes);
out_err0: out_no_sizes:
kfree(user_srf); kfree(user_srf);
out_no_user_srf:
ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
out_unlock:
ttm_read_unlock(&vmaster->lock);
return ret; return ret;
} }
...@@ -1690,8 +1772,11 @@ static void vmw_user_stream_free(struct vmw_resource *res) ...@@ -1690,8 +1772,11 @@ static void vmw_user_stream_free(struct vmw_resource *res)
{ {
struct vmw_user_stream *stream = struct vmw_user_stream *stream =
container_of(res, struct vmw_user_stream, stream.res); container_of(res, struct vmw_user_stream, stream.res);
struct vmw_private *dev_priv = res->dev_priv;
kfree(stream); kfree(stream);
ttm_mem_global_free(vmw_mem_glob(dev_priv),
vmw_user_stream_size);
} }
/** /**
...@@ -1745,23 +1830,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, ...@@ -1745,23 +1830,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
struct vmw_private *dev_priv = vmw_priv(dev); struct vmw_private *dev_priv = vmw_priv(dev);
struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); struct vmw_user_stream *stream;
struct vmw_resource *res; struct vmw_resource *res;
struct vmw_resource *tmp; struct vmw_resource *tmp;
struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_master *vmaster = vmw_master(file_priv->master);
int ret; int ret;
if (unlikely(stream == NULL)) /*
return -ENOMEM; * Approximate idr memory usage with 128 bytes. It will be limited
* by maximum number_of streams anyway?
*/
if (unlikely(vmw_user_stream_size == 0))
vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128;
ret = ttm_read_lock(&vmaster->lock, true);
if (unlikely(ret != 0))
return ret;
ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
vmw_user_stream_size,
false, true);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Out of graphics memory for stream"
" creation.\n");
goto out_unlock;
}
stream = kmalloc(sizeof(*stream), GFP_KERNEL);
if (unlikely(stream == NULL)) {
ttm_mem_global_free(vmw_mem_glob(dev_priv),
vmw_user_stream_size);
ret = -ENOMEM;
goto out_unlock;
}
res = &stream->stream.res; res = &stream->stream.res;
stream->base.shareable = false; stream->base.shareable = false;
stream->base.tfile = NULL; stream->base.tfile = NULL;
/*
* From here on, the destructor takes over resource freeing.
*/
ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);
if (unlikely(ret != 0)) if (unlikely(ret != 0))
return ret; goto out_unlock;
tmp = vmw_resource_reference(res); tmp = vmw_resource_reference(res);
ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM,
...@@ -1775,6 +1893,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, ...@@ -1775,6 +1893,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
arg->stream_id = res->id; arg->stream_id = res->id;
out_err: out_err:
vmw_resource_unreference(&res); vmw_resource_unreference(&res);
out_unlock:
ttm_read_unlock(&vmaster->lock);
return ret; return ret;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册