Home | History | Annotate | Line # | Download | only in vmwgfx
vmwgfx_context.c revision 1.1.1.1.8.2
      1 /**************************************************************************
      2  *
      3  * Copyright  2009-2012 VMware, Inc., Palo Alto, CA., USA
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 #include "vmwgfx_drv.h"
     29 #include "vmwgfx_resource_priv.h"
     30 #include "ttm/ttm_placement.h"
     31 
     32 struct vmw_user_context {
     33 	struct ttm_base_object base;
     34 	struct vmw_resource res;
     35 };
     36 
     37 static void vmw_user_context_free(struct vmw_resource *res);
     38 static struct vmw_resource *
     39 vmw_user_context_base_to_res(struct ttm_base_object *base);
     40 
     41 static uint64_t vmw_user_context_size;
     42 
     43 static const struct vmw_user_resource_conv user_context_conv = {
     44 	.object_type = VMW_RES_CONTEXT,
     45 	.base_obj_to_res = vmw_user_context_base_to_res,
     46 	.res_free = vmw_user_context_free
     47 };
     48 
     49 const struct vmw_user_resource_conv *user_context_converter =
     50 	&user_context_conv;
     51 
     52 
     53 static const struct vmw_res_func vmw_legacy_context_func = {
     54 	.res_type = vmw_res_context,
     55 	.needs_backup = false,
     56 	.may_evict = false,
     57 	.type_name = "legacy contexts",
     58 	.backup_placement = NULL,
     59 	.create = NULL,
     60 	.destroy = NULL,
     61 	.bind = NULL,
     62 	.unbind = NULL
     63 };
     64 
     65 /**
     66  * Context management:
     67  */
     68 
     69 static void vmw_hw_context_destroy(struct vmw_resource *res)
     70 {
     71 
     72 	struct vmw_private *dev_priv = res->dev_priv;
     73 	struct {
     74 		SVGA3dCmdHeader header;
     75 		SVGA3dCmdDestroyContext body;
     76 	} *cmd;
     77 
     78 
     79 	vmw_execbuf_release_pinned_bo(dev_priv);
     80 	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
     81 	if (unlikely(cmd == NULL)) {
     82 		DRM_ERROR("Failed reserving FIFO space for surface "
     83 			  "destruction.\n");
     84 		return;
     85 	}
     86 
     87 	cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY);
     88 	cmd->header.size = cpu_to_le32(sizeof(cmd->body));
     89 	cmd->body.cid = cpu_to_le32(res->id);
     90 
     91 	vmw_fifo_commit(dev_priv, sizeof(*cmd));
     92 	vmw_3d_resource_dec(dev_priv, false);
     93 }
     94 
     95 static int vmw_context_init(struct vmw_private *dev_priv,
     96 			    struct vmw_resource *res,
     97 			    void (*res_free) (struct vmw_resource *res))
     98 {
     99 	int ret;
    100 
    101 	struct {
    102 		SVGA3dCmdHeader header;
    103 		SVGA3dCmdDefineContext body;
    104 	} *cmd;
    105 
    106 	ret = vmw_resource_init(dev_priv, res, false,
    107 				res_free, &vmw_legacy_context_func);
    108 
    109 	if (unlikely(ret != 0)) {
    110 		DRM_ERROR("Failed to allocate a resource id.\n");
    111 		goto out_early;
    112 	}
    113 
    114 	if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) {
    115 		DRM_ERROR("Out of hw context ids.\n");
    116 		vmw_resource_unreference(&res);
    117 		return -ENOMEM;
    118 	}
    119 
    120 	cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
    121 	if (unlikely(cmd == NULL)) {
    122 		DRM_ERROR("Fifo reserve failed.\n");
    123 		vmw_resource_unreference(&res);
    124 		return -ENOMEM;
    125 	}
    126 
    127 	cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE);
    128 	cmd->header.size = cpu_to_le32(sizeof(cmd->body));
    129 	cmd->body.cid = cpu_to_le32(res->id);
    130 
    131 	vmw_fifo_commit(dev_priv, sizeof(*cmd));
    132 	(void) vmw_3d_resource_inc(dev_priv, false);
    133 	vmw_resource_activate(res, vmw_hw_context_destroy);
    134 	return 0;
    135 
    136 out_early:
    137 	if (res_free == NULL)
    138 		kfree(res);
    139 	else
    140 		res_free(res);
    141 	return ret;
    142 }
    143 
    144 struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
    145 {
    146 	struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
    147 	int ret;
    148 
    149 	if (unlikely(res == NULL))
    150 		return NULL;
    151 
    152 	ret = vmw_context_init(dev_priv, res, NULL);
    153 
    154 	return (ret == 0) ? res : NULL;
    155 }
    156 
    157 /**
    158  * User-space context management:
    159  */
    160 
    161 static struct vmw_resource *
    162 vmw_user_context_base_to_res(struct ttm_base_object *base)
    163 {
    164 	return &(container_of(base, struct vmw_user_context, base)->res);
    165 }
    166 
    167 static void vmw_user_context_free(struct vmw_resource *res)
    168 {
    169 	struct vmw_user_context *ctx =
    170 	    container_of(res, struct vmw_user_context, res);
    171 	struct vmw_private *dev_priv = res->dev_priv;
    172 
    173 	ttm_base_object_kfree(ctx, base);
    174 	ttm_mem_global_free(vmw_mem_glob(dev_priv),
    175 			    vmw_user_context_size);
    176 }
    177 
    178 /**
    179  * This function is called when user space has no more references on the
    180  * base object. It releases the base-object's reference on the resource object.
    181  */
    182 
    183 static void vmw_user_context_base_release(struct ttm_base_object **p_base)
    184 {
    185 	struct ttm_base_object *base = *p_base;
    186 	struct vmw_user_context *ctx =
    187 	    container_of(base, struct vmw_user_context, base);
    188 	struct vmw_resource *res = &ctx->res;
    189 
    190 	*p_base = NULL;
    191 	vmw_resource_unreference(&res);
    192 }
    193 
    194 int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
    195 			      struct drm_file *file_priv)
    196 {
    197 	struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
    198 	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
    199 
    200 	return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE);
    201 }
    202 
    203 int vmw_context_define_ioctl(struct drm_device *dev, void *data,
    204 			     struct drm_file *file_priv)
    205 {
    206 	struct vmw_private *dev_priv = vmw_priv(dev);
    207 	struct vmw_user_context *ctx;
    208 	struct vmw_resource *res;
    209 	struct vmw_resource *tmp;
    210 	struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
    211 	struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
    212 	struct vmw_master *vmaster = vmw_master(file_priv->master);
    213 	int ret;
    214 
    215 
    216 	/*
    217 	 * Approximate idr memory usage with 128 bytes. It will be limited
    218 	 * by maximum number_of contexts anyway.
    219 	 */
    220 
    221 	if (unlikely(vmw_user_context_size == 0))
    222 		vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128;
    223 
    224 	ret = ttm_read_lock(&vmaster->lock, true);
    225 	if (unlikely(ret != 0))
    226 		return ret;
    227 
    228 	ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv),
    229 				   vmw_user_context_size,
    230 				   false, true);
    231 	if (unlikely(ret != 0)) {
    232 		if (ret != -ERESTARTSYS)
    233 			DRM_ERROR("Out of graphics memory for context"
    234 				  " creation.\n");
    235 		goto out_unlock;
    236 	}
    237 
    238 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
    239 	if (unlikely(ctx == NULL)) {
    240 		ttm_mem_global_free(vmw_mem_glob(dev_priv),
    241 				    vmw_user_context_size);
    242 		ret = -ENOMEM;
    243 		goto out_unlock;
    244 	}
    245 
    246 	res = &ctx->res;
    247 	ctx->base.shareable = false;
    248 	ctx->base.tfile = NULL;
    249 
    250 	/*
    251 	 * From here on, the destructor takes over resource freeing.
    252 	 */
    253 
    254 	ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
    255 	if (unlikely(ret != 0))
    256 		goto out_unlock;
    257 
    258 	tmp = vmw_resource_reference(&ctx->res);
    259 	ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
    260 				   &vmw_user_context_base_release, NULL);
    261 
    262 	if (unlikely(ret != 0)) {
    263 		vmw_resource_unreference(&tmp);
    264 		goto out_err;
    265 	}
    266 
    267 	arg->cid = ctx->base.hash.key;
    268 out_err:
    269 	vmw_resource_unreference(&res);
    270 out_unlock:
    271 	ttm_read_unlock(&vmaster->lock);
    272 	return ret;
    273 
    274 }
    275