[PATCH drm-misc-next v3 6/7] drm/gpuvm: generalize dma_resv/extobj handling and GEM validation

kernel test robot lkp at intel.com
Sat Sep 9 20:16:48 UTC 2023


Hi Danilo,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 6bd3d8da51ca1ec97c724016466606aec7739b9f]

url:    https://github.com/intel-lab-lkp/linux/commits/Danilo-Krummrich/drm-gpuvm-rename-struct-drm_gpuva_manager-to-struct-drm_gpuvm/20230909-233346
base:   6bd3d8da51ca1ec97c724016466606aec7739b9f
patch link:    https://lore.kernel.org/r/20230909153125.30032-7-dakr%40redhat.com
patch subject: [PATCH drm-misc-next v3 6/7] drm/gpuvm: generalize dma_resv/extobj handling and GEM validation
config: riscv-defconfig (https://download.01.org/0day-ci/archive/20230910/202309100424.uNXGR9d4-lkp@intel.com/config)
compiler: riscv64-linux-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20230910/202309100424.uNXGR9d4-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp at intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202309100424.uNXGR9d4-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/gpu/drm/drm_gpuvm.c:734: warning: Function parameter or member '__gpuvm' not described in 'for_each_vm_bo_in_list'
>> drivers/gpu/drm/drm_gpuvm.c:734: warning: Function parameter or member '__list_name' not described in 'for_each_vm_bo_in_list'
>> drivers/gpu/drm/drm_gpuvm.c:734: warning: Function parameter or member '__local_list' not described in 'for_each_vm_bo_in_list'
>> drivers/gpu/drm/drm_gpuvm.c:734: warning: Function parameter or member '__vm_bo' not described in 'for_each_vm_bo_in_list'


vim +734 drivers/gpu/drm/drm_gpuvm.c

    32	
    33	/**
    34	 * DOC: Overview
    35	 *
    36	 * The DRM GPU VA Manager, represented by struct drm_gpuvm keeps track of a
    37	 * GPU's virtual address (VA) space and manages the corresponding virtual
    38	 * mappings represented by &drm_gpuva objects. It also keeps track of the
    39	 * mapping's backing &drm_gem_object buffers.
    40	 *
    41	 * &drm_gem_object buffers maintain a list of &drm_gpuva objects representing
    42	 * all existent GPU VA mappings using this &drm_gem_object as backing buffer.
    43	 *
    44	 * GPU VAs can be flagged as sparse, such that drivers may use GPU VAs to also
    45	 * keep track of sparse PTEs in order to support Vulkan 'Sparse Resources'.
    46	 *
    47	 * The GPU VA manager internally uses a rb-tree to manage the
    48	 * &drm_gpuva mappings within a GPU's virtual address space.
    49	 *
    50	 * The &drm_gpuvm structure contains a special &drm_gpuva representing the
    51	 * portion of VA space reserved by the kernel. This node is initialized together
    52	 * with the GPU VA manager instance and removed when the GPU VA manager is
    53	 * destroyed.
    54	 *
    55	 * In a typical application drivers would embed struct drm_gpuvm and
    56	 * struct drm_gpuva within their own driver specific structures, there won't be
    57	 * any memory allocations of its own nor memory allocations of &drm_gpuva
    58	 * entries.
    59	 *
    60	 * The data structures needed to store &drm_gpuvas within the &drm_gpuvm are
    61	 * contained within struct drm_gpuva already. Hence, for inserting &drm_gpuva
    62	 * entries from within dma-fence signalling critical sections it is enough to
    63	 * pre-allocate the &drm_gpuva structures.
    64	 *
    65	 * In order to connect a struct drm_gpuva its backing &drm_gem_object each
    66	 * &drm_gem_object maintains a list of &drm_gpuvm_bo structures, and each
    67	 * &drm_gpuvm_bo contains a list of &&drm_gpuva structures.
    68	 *
    69	 * A &drm_gpuvm_bo is an abstraction that represents a combination of a
    70	 * &drm_gpuvm and a &drm_gem_object. Every such combination should be unique.
    71	 * This is ensured by the API through drm_gpuvm_bo_obtain() and
    72	 * drm_gpuvm_bo_obtain_prealloc() which first look into the corresponding
    73	 * &drm_gem_object list of &drm_gpuvm_bos for an existing instance of this
    74	 * particular combination. If not existent a new instance is created and linked
    75	 * to the &drm_gem_object.
    76	 *
    77	 * &drm_gpuvm_bo structures, since unique for a given &drm_gpuvm, are also used
    78	 * as entry for the &drm_gpuvm's lists of external and evicted objects. Those
    79	 * list are maintained in order to accelerate locking of dma-resv locks and
    80	 * validation of evicted objects bound in a &drm_gpuvm. For instance the all
    81	 * &drm_gem_object's &dma_resv of a given &drm_gpuvm can be locked by calling
    82	 * drm_gpuvm_exec_lock(). Once locked drivers can call drm_gpuvm_validate() in
    83	 * order to validate all evicted &drm_gem_objects. It is also possible to lock
    84	 * additional &drm_gem_objects by providing the corresponding parameters to
    85	 * drm_gpuvm_exec_lock() as well as open code the &drm_exec loop while making
    86	 * use of helper functions such as drm_gpuvm_prepare_range() or
    87	 * drm_gpuvm_prepare_objects().
    88	 *
    89	 * Every bound &drm_gem_object is treated as external object when its &dma_resv
    90	 * structure is different than the &drm_gpuvm's common &dma_resv structure.
    91	 */
    92	
    93	/**
    94	 * DOC: Split and Merge
    95	 *
    96	 * Besides its capability to manage and represent a GPU VA space, the
    97	 * GPU VA manager also provides functions to let the &drm_gpuvm calculate a
    98	 * sequence of operations to satisfy a given map or unmap request.
    99	 *
   100	 * Therefore the DRM GPU VA manager provides an algorithm implementing splitting
   101	 * and merging of existent GPU VA mappings with the ones that are requested to
   102	 * be mapped or unmapped. This feature is required by the Vulkan API to
   103	 * implement Vulkan 'Sparse Memory Bindings' - drivers UAPIs often refer to this
   104	 * as VM BIND.
   105	 *
   106	 * Drivers can call drm_gpuvm_sm_map() to receive a sequence of callbacks
   107	 * containing map, unmap and remap operations for a given newly requested
   108	 * mapping. The sequence of callbacks represents the set of operations to
   109	 * execute in order to integrate the new mapping cleanly into the current state
   110	 * of the GPU VA space.
   111	 *
   112	 * Depending on how the new GPU VA mapping intersects with the existent mappings
   113	 * of the GPU VA space the &drm_gpuvm_ops callbacks contain an arbitrary amount
   114	 * of unmap operations, a maximum of two remap operations and a single map
   115	 * operation. The caller might receive no callback at all if no operation is
   116	 * required, e.g. if the requested mapping already exists in the exact same way.
   117	 *
   118	 * The single map operation represents the original map operation requested by
   119	 * the caller.
   120	 *
   121	 * &drm_gpuva_op_unmap contains a 'keep' field, which indicates whether the
   122	 * &drm_gpuva to unmap is physically contiguous with the original mapping
   123	 * request. Optionally, if 'keep' is set, drivers may keep the actual page table
   124	 * entries for this &drm_gpuva, adding the missing page table entries only and
   125	 * update the &drm_gpuvm's view of things accordingly.
   126	 *
   127	 * Drivers may do the same optimization, namely delta page table updates, also
   128	 * for remap operations. This is possible since &drm_gpuva_op_remap consists of
   129	 * one unmap operation and one or two map operations, such that drivers can
   130	 * derive the page table update delta accordingly.
   131	 *
   132	 * Note that there can't be more than two existent mappings to split up, one at
   133	 * the beginning and one at the end of the new mapping, hence there is a
   134	 * maximum of two remap operations.
   135	 *
   136	 * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses &drm_gpuvm_ops to
   137	 * call back into the driver in order to unmap a range of GPU VA space. The
   138	 * logic behind this function is way simpler though: For all existent mappings
   139	 * enclosed by the given range unmap operations are created. For mappings which
   140	 * are only partically located within the given range, remap operations are
   141	 * created such that those mappings are split up and re-mapped partically.
   142	 *
   143	 * As an alternative to drm_gpuvm_sm_map() and drm_gpuvm_sm_unmap(),
   144	 * drm_gpuvm_sm_map_ops_create() and drm_gpuvm_sm_unmap_ops_create() can be used
   145	 * to directly obtain an instance of struct drm_gpuva_ops containing a list of
   146	 * &drm_gpuva_op, which can be iterated with drm_gpuva_for_each_op(). This list
   147	 * contains the &drm_gpuva_ops analogous to the callbacks one would receive when
   148	 * calling drm_gpuvm_sm_map() or drm_gpuvm_sm_unmap(). While this way requires
   149	 * more memory (to allocate the &drm_gpuva_ops), it provides drivers a way to
   150	 * iterate the &drm_gpuva_op multiple times, e.g. once in a context where memory
   151	 * allocations are possible (e.g. to allocate GPU page tables) and once in the
   152	 * dma-fence signalling critical path.
   153	 *
   154	 * To update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert() and
   155	 * drm_gpuva_remove() may be used. These functions can safely be used from
   156	 * &drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or
   157	 * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the
   158	 * provided helper functions drm_gpuva_map(), drm_gpuva_remap() and
   159	 * drm_gpuva_unmap() instead.
   160	 *
   161	 * The following diagram depicts the basic relationships of existent GPU VA
   162	 * mappings, a newly requested mapping and the resulting mappings as implemented
   163	 * by drm_gpuvm_sm_map() - it doesn't cover any arbitrary combinations of these.
   164	 *
   165	 * 1) Requested mapping is identical. Replace it, but indicate the backing PTEs
   166	 *    could be kept.
   167	 *
   168	 *    ::
   169	 *
   170	 *	     0     a     1
   171	 *	old: |-----------| (bo_offset=n)
   172	 *
   173	 *	     0     a     1
   174	 *	req: |-----------| (bo_offset=n)
   175	 *
   176	 *	     0     a     1
   177	 *	new: |-----------| (bo_offset=n)
   178	 *
   179	 *
   180	 * 2) Requested mapping is identical, except for the BO offset, hence replace
   181	 *    the mapping.
   182	 *
   183	 *    ::
   184	 *
   185	 *	     0     a     1
   186	 *	old: |-----------| (bo_offset=n)
   187	 *
   188	 *	     0     a     1
   189	 *	req: |-----------| (bo_offset=m)
   190	 *
   191	 *	     0     a     1
   192	 *	new: |-----------| (bo_offset=m)
   193	 *
   194	 *
   195	 * 3) Requested mapping is identical, except for the backing BO, hence replace
   196	 *    the mapping.
   197	 *
   198	 *    ::
   199	 *
   200	 *	     0     a     1
   201	 *	old: |-----------| (bo_offset=n)
   202	 *
   203	 *	     0     b     1
   204	 *	req: |-----------| (bo_offset=n)
   205	 *
   206	 *	     0     b     1
   207	 *	new: |-----------| (bo_offset=n)
   208	 *
   209	 *
   210	 * 4) Existent mapping is a left aligned subset of the requested one, hence
   211	 *    replace the existent one.
   212	 *
   213	 *    ::
   214	 *
   215	 *	     0  a  1
   216	 *	old: |-----|       (bo_offset=n)
   217	 *
   218	 *	     0     a     2
   219	 *	req: |-----------| (bo_offset=n)
   220	 *
   221	 *	     0     a     2
   222	 *	new: |-----------| (bo_offset=n)
   223	 *
   224	 *    .. note::
   225	 *       We expect to see the same result for a request with a different BO
   226	 *       and/or non-contiguous BO offset.
   227	 *
   228	 *
   229	 * 5) Requested mapping's range is a left aligned subset of the existent one,
   230	 *    but backed by a different BO. Hence, map the requested mapping and split
   231	 *    the existent one adjusting its BO offset.
   232	 *
   233	 *    ::
   234	 *
   235	 *	     0     a     2
   236	 *	old: |-----------| (bo_offset=n)
   237	 *
   238	 *	     0  b  1
   239	 *	req: |-----|       (bo_offset=n)
   240	 *
   241	 *	     0  b  1  a' 2
   242	 *	new: |-----|-----| (b.bo_offset=n, a.bo_offset=n+1)
   243	 *
   244	 *    .. note::
   245	 *       We expect to see the same result for a request with a different BO
   246	 *       and/or non-contiguous BO offset.
   247	 *
   248	 *
   249	 * 6) Existent mapping is a superset of the requested mapping. Split it up, but
   250	 *    indicate that the backing PTEs could be kept.
   251	 *
   252	 *    ::
   253	 *
   254	 *	     0     a     2
   255	 *	old: |-----------| (bo_offset=n)
   256	 *
   257	 *	     0  a  1
   258	 *	req: |-----|       (bo_offset=n)
   259	 *
   260	 *	     0  a  1  a' 2
   261	 *	new: |-----|-----| (a.bo_offset=n, a'.bo_offset=n+1)
   262	 *
   263	 *
   264	 * 7) Requested mapping's range is a right aligned subset of the existent one,
   265	 *    but backed by a different BO. Hence, map the requested mapping and split
   266	 *    the existent one, without adjusting the BO offset.
   267	 *
   268	 *    ::
   269	 *
   270	 *	     0     a     2
   271	 *	old: |-----------| (bo_offset=n)
   272	 *
   273	 *	           1  b  2
   274	 *	req:       |-----| (bo_offset=m)
   275	 *
   276	 *	     0  a  1  b  2
   277	 *	new: |-----|-----| (a.bo_offset=n,b.bo_offset=m)
   278	 *
   279	 *
   280	 * 8) Existent mapping is a superset of the requested mapping. Split it up, but
   281	 *    indicate that the backing PTEs could be kept.
   282	 *
   283	 *    ::
   284	 *
   285	 *	      0     a     2
   286	 *	old: |-----------| (bo_offset=n)
   287	 *
   288	 *	           1  a  2
   289	 *	req:       |-----| (bo_offset=n+1)
   290	 *
   291	 *	     0  a' 1  a  2
   292	 *	new: |-----|-----| (a'.bo_offset=n, a.bo_offset=n+1)
   293	 *
   294	 *
   295	 * 9) Existent mapping is overlapped at the end by the requested mapping backed
   296	 *    by a different BO. Hence, map the requested mapping and split up the
   297	 *    existent one, without adjusting the BO offset.
   298	 *
   299	 *    ::
   300	 *
   301	 *	     0     a     2
   302	 *	old: |-----------|       (bo_offset=n)
   303	 *
   304	 *	           1     b     3
   305	 *	req:       |-----------| (bo_offset=m)
   306	 *
   307	 *	     0  a  1     b     3
   308	 *	new: |-----|-----------| (a.bo_offset=n,b.bo_offset=m)
   309	 *
   310	 *
   311	 * 10) Existent mapping is overlapped by the requested mapping, both having the
   312	 *     same backing BO with a contiguous offset. Indicate the backing PTEs of
   313	 *     the old mapping could be kept.
   314	 *
   315	 *     ::
   316	 *
   317	 *	      0     a     2
   318	 *	 old: |-----------|       (bo_offset=n)
   319	 *
   320	 *	            1     a     3
   321	 *	 req:       |-----------| (bo_offset=n+1)
   322	 *
   323	 *	      0  a' 1     a     3
   324	 *	 new: |-----|-----------| (a'.bo_offset=n, a.bo_offset=n+1)
   325	 *
   326	 *
   327	 * 11) Requested mapping's range is a centered subset of the existent one
   328	 *     having a different backing BO. Hence, map the requested mapping and split
   329	 *     up the existent one in two mappings, adjusting the BO offset of the right
   330	 *     one accordingly.
   331	 *
   332	 *     ::
   333	 *
   334	 *	      0        a        3
   335	 *	 old: |-----------------| (bo_offset=n)
   336	 *
   337	 *	            1  b  2
   338	 *	 req:       |-----|       (bo_offset=m)
   339	 *
   340	 *	      0  a  1  b  2  a' 3
   341	 *	 new: |-----|-----|-----| (a.bo_offset=n,b.bo_offset=m,a'.bo_offset=n+2)
   342	 *
   343	 *
   344	 * 12) Requested mapping is a contiguous subset of the existent one. Split it
   345	 *     up, but indicate that the backing PTEs could be kept.
   346	 *
   347	 *     ::
   348	 *
   349	 *	      0        a        3
   350	 *	 old: |-----------------| (bo_offset=n)
   351	 *
   352	 *	            1  a  2
   353	 *	 req:       |-----|       (bo_offset=n+1)
   354	 *
   355	 *	      0  a' 1  a  2 a'' 3
   356	 *	 old: |-----|-----|-----| (a'.bo_offset=n, a.bo_offset=n+1, a''.bo_offset=n+2)
   357	 *
   358	 *
   359	 * 13) Existent mapping is a right aligned subset of the requested one, hence
   360	 *     replace the existent one.
   361	 *
   362	 *     ::
   363	 *
   364	 *	            1  a  2
   365	 *	 old:       |-----| (bo_offset=n+1)
   366	 *
   367	 *	      0     a     2
   368	 *	 req: |-----------| (bo_offset=n)
   369	 *
   370	 *	      0     a     2
   371	 *	 new: |-----------| (bo_offset=n)
   372	 *
   373	 *     .. note::
   374	 *        We expect to see the same result for a request with a different bo
   375	 *        and/or non-contiguous bo_offset.
   376	 *
   377	 *
   378	 * 14) Existent mapping is a centered subset of the requested one, hence
   379	 *     replace the existent one.
   380	 *
   381	 *     ::
   382	 *
   383	 *	            1  a  2
   384	 *	 old:       |-----| (bo_offset=n+1)
   385	 *
   386	 *	      0        a       3
   387	 *	 req: |----------------| (bo_offset=n)
   388	 *
   389	 *	      0        a       3
   390	 *	 new: |----------------| (bo_offset=n)
   391	 *
   392	 *     .. note::
   393	 *        We expect to see the same result for a request with a different bo
   394	 *        and/or non-contiguous bo_offset.
   395	 *
   396	 *
   397	 * 15) Existent mappings is overlapped at the beginning by the requested mapping
   398	 *     backed by a different BO. Hence, map the requested mapping and split up
   399	 *     the existent one, adjusting its BO offset accordingly.
   400	 *
   401	 *     ::
   402	 *
   403	 *	            1     a     3
   404	 *	 old:       |-----------| (bo_offset=n)
   405	 *
   406	 *	      0     b     2
   407	 *	 req: |-----------|       (bo_offset=m)
   408	 *
   409	 *	      0     b     2  a' 3
   410	 *	 new: |-----------|-----| (b.bo_offset=m,a.bo_offset=n+2)
   411	 */
   412	
   413	/**
   414	 * DOC: Locking
   415	 *
   416	 * Generally, the GPU VA manager does not take care of locking itself, it is
   417	 * the drivers responsibility to take care about locking. Drivers might want to
   418	 * protect the following operations: inserting, removing and iterating
   419	 * &drm_gpuva objects as well as generating all kinds of operations, such as
   420	 * split / merge or prefetch.
   421	 *
   422	 * The GPU VA manager also does not take care of the locking of the backing
   423	 * &drm_gem_object buffers GPU VA lists and &drm_gpuvm_bo abstractions by
   424	 * itself; drivers are responsible to enforce mutual exclusion using either the
   425	 * GEMs dma_resv lock or alternatively a driver specific external lock. For the
   426	 * latter see also drm_gem_gpuva_set_lock().
   427	 *
   428	 * However, the GPU VA manager contains lockdep checks to ensure callers of its
   429	 * API hold the corresponding lock whenever the &drm_gem_objects GPU VA list is
   430	 * accessed by functions such as drm_gpuva_link() or drm_gpuva_unlink(), but
   431	 * also drm_gpuvm_bo_obtain() and drm_gpuvm_bo_put().
   432	 *
   433	 * The latter is required since on creation and destruction of a &drm_gpuvm_bo
   434	 * the &drm_gpuvm_bo is attached / removed from the &drm_gem_objects gpuva list.
   435	 * Subsequent calls to drm_gpuvm_bo_obtain() for the same &drm_gpuvm and
   436	 * &drm_gem_object must be able to observe previous creations and destructions
   437	 * of &drm_gpuvm_bos in order to keep instances unique.
   438	 *
   439	 * The &drm_gpuvm's lists for keeping track of external and evicted objects are
   440	 * protected against concurrent insertion / removal and iteration internally.
   441	 *
   442	 * However, drivers still need ensure to protect concurrent calls to functions
   443	 * iterating those lists, such as drm_gpuvm_validate() and
   444	 * drm_gpuvm_prepare_objects(). Every such function contains a particular
   445	 * comment and lockdep checks if possible.
   446	 *
   447	 * Functions adding or removing entries from those lists, such as
   448	 * drm_gpuvm_bo_evict() or drm_gpuvm_bo_extobj_add() may be called with external
   449	 * locks being held, e.g. in order to avoid the corresponding list to be
   450	 * (safely) modified while potentially being iternated by other API functions.
   451	 * However, this is entirely optional.
   452	 */
   453	
   454	/**
   455	 * DOC: Examples
   456	 *
   457	 * This section gives two examples on how to let the DRM GPUVA Manager generate
   458	 * &drm_gpuva_op in order to satisfy a given map or unmap request and how to
   459	 * make use of them.
   460	 *
   461	 * The below code is strictly limited to illustrate the generic usage pattern.
   462	 * To maintain simplicitly, it doesn't make use of any abstractions for common
   463	 * code, different (asyncronous) stages with fence signalling critical paths,
   464	 * any other helpers or error handling in terms of freeing memory and dropping
   465	 * previously taken locks.
   466	 *
   467	 * 1) Obtain a list of &drm_gpuva_op to create a new mapping::
   468	 *
   469	 *	// Allocates a new &drm_gpuva.
   470	 *	struct drm_gpuva * driver_gpuva_alloc(void);
   471	 *
   472	 *	// Typically drivers would embedd the &drm_gpuvm and &drm_gpuva
   473	 *	// structure in individual driver structures and lock the dma-resv with
   474	 *	// drm_exec or similar helpers.
   475	 *	int driver_mapping_create(struct drm_gpuvm *gpuvm,
   476	 *				  u64 addr, u64 range,
   477	 *				  struct drm_gem_object *obj, u64 offset)
   478	 *	{
   479	 *		struct drm_gpuva_ops *ops;
   480	 *		struct drm_gpuva_op *op
   481	 *		struct drm_gpuvm_bo *vm_bo;
   482	 *
   483	 *		driver_lock_va_space();
   484	 *		ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr, range,
   485	 *						  obj, offset);
   486	 *		if (IS_ERR(ops))
   487	 *			return PTR_ERR(ops);
   488	 *
   489	 *		vm_bo = drm_gpuvm_bo_obtain(gpuvm, obj);
   490	 *		if (IS_ERR(vm_bo))
   491	 *			return PTR_ERR(vm_bo);
   492	 *
   493	 *		drm_gpuva_for_each_op(op, ops) {
   494	 *			struct drm_gpuva *va;
   495	 *
   496	 *			switch (op->op) {
   497	 *			case DRM_GPUVA_OP_MAP:
   498	 *				va = driver_gpuva_alloc();
   499	 *				if (!va)
   500	 *					; // unwind previous VA space updates,
   501	 *					  // free memory and unlock
   502	 *
   503	 *				driver_vm_map();
   504	 *				drm_gpuva_map(gpuvm, va, &op->map);
   505	 *				drm_gpuva_link(va, vm_bo);
   506	 *
   507	 *				break;
   508	 *			case DRM_GPUVA_OP_REMAP: {
   509	 *				struct drm_gpuva *prev = NULL, *next = NULL;
   510	 *
   511	 *				va = op->remap.unmap->va;
   512	 *
   513	 *				if (op->remap.prev) {
   514	 *					prev = driver_gpuva_alloc();
   515	 *					if (!prev)
   516	 *						; // unwind previous VA space
   517	 *						  // updates, free memory and
   518	 *						  // unlock
   519	 *				}
   520	 *
   521	 *				if (op->remap.next) {
   522	 *					next = driver_gpuva_alloc();
   523	 *					if (!next)
   524	 *						; // unwind previous VA space
   525	 *						  // updates, free memory and
   526	 *						  // unlock
   527	 *				}
   528	 *
   529	 *				driver_vm_remap();
   530	 *				drm_gpuva_remap(prev, next, &op->remap);
   531	 *
   532	 *				if (prev)
   533	 *					drm_gpuva_link(prev, va->vm_bo);
   534	 *				if (next)
   535	 *					drm_gpuva_link(next, va->vm_bo);
   536	 *				drm_gpuva_unlink(va);
   537	 *
   538	 *				break;
   539	 *			}
   540	 *			case DRM_GPUVA_OP_UNMAP:
   541	 *				va = op->unmap->va;
   542	 *
   543	 *				driver_vm_unmap();
   544	 *				drm_gpuva_unlink(va);
   545	 *				drm_gpuva_unmap(&op->unmap);
   546	 *
   547	 *				break;
   548	 *			default:
   549	 *				break;
   550	 *			}
   551	 *		}
   552	 *		drm_gpuvm_bo_put(vm_bo);
   553	 *		driver_unlock_va_space();
   554	 *
   555	 *		return 0;
   556	 *	}
   557	 *
   558	 * 2) Receive a callback for each &drm_gpuva_op to create a new mapping::
   559	 *
   560	 *	struct driver_context {
   561	 *		struct drm_gpuvm *gpuvm;
   562	 *		struct drm_gpuvm_bo *vm_bo;
   563	 *		struct drm_gpuva *new_va;
   564	 *		struct drm_gpuva *prev_va;
   565	 *		struct drm_gpuva *next_va;
   566	 *	};
   567	 *
   568	 *	// ops to pass to drm_gpuvm_init()
   569	 *	static const struct drm_gpuvm_ops driver_gpuvm_ops = {
   570	 *		.sm_step_map = driver_gpuva_map,
   571	 *		.sm_step_remap = driver_gpuva_remap,
   572	 *		.sm_step_unmap = driver_gpuva_unmap,
   573	 *	};
   574	 *
   575	 *	// Typically drivers would embedd the &drm_gpuvm and &drm_gpuva
   576	 *	// structure in individual driver structures and lock the dma-resv with
   577	 *	// drm_exec or similar helpers.
   578	 *	int driver_mapping_create(struct drm_gpuvm *gpuvm,
   579	 *				  u64 addr, u64 range,
   580	 *				  struct drm_gem_object *obj, u64 offset)
   581	 *	{
   582	 *		struct driver_context ctx;
   583	 *		struct drm_gpuvm_bo *vm_bo;
   584	 *		struct drm_gpuva_ops *ops;
   585	 *		struct drm_gpuva_op *op;
   586	 *		int ret = 0;
   587	 *
   588	 *		ctx.gpuvm = gpuvm;
   589	 *
   590	 *		ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL);
   591	 *		ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL);
   592	 *		ctx.next_va = kzalloc(sizeof(*ctx.next_va), GFP_KERNEL);
   593	 *		ctx.vm_bo = drm_gpuvm_bo_create(gpuvm, obj);
   594	 *		if (!ctx.new_va || !ctx.prev_va || !ctx.next_va || !vm_bo) {
   595	 *			ret = -ENOMEM;
   596	 *			goto out;
   597	 *		}
   598	 *
   599	 *		// Typically protected with a driver specific GEM gpuva lock
   600	 *		// used in the fence signaling path for drm_gpuva_link() and
   601	 *		// drm_gpuva_unlink(), hence pre-allocate.
   602	 *		ctx.vm_bo = drm_gpuvm_bo_obtain_prealloc(ctx.vm_bo);
   603	 *
   604	 *		driver_lock_va_space();
   605	 *		ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range, obj, offset);
   606	 *		driver_unlock_va_space();
   607	 *
   608	 *	out:
   609	 *		drm_gpuvm_bo_put(ctx.vm_bo);
   610	 *		kfree(ctx.new_va);
   611	 *		kfree(ctx.prev_va);
   612	 *		kfree(ctx.next_va);
   613	 *		return ret;
   614	 *	}
   615	 *
   616	 *	int driver_gpuva_map(struct drm_gpuva_op *op, void *__ctx)
   617	 *	{
   618	 *		struct driver_context *ctx = __ctx;
   619	 *
   620	 *		drm_gpuva_map(ctx->vm, ctx->new_va, &op->map);
   621	 *
   622	 *		drm_gpuva_link(ctx->new_va, ctx->vm_bo);
   623	 *
   624	 *		// prevent the new GPUVA from being freed in
   625	 *		// driver_mapping_create()
   626	 *		ctx->new_va = NULL;
   627	 *
   628	 *		return 0;
   629	 *	}
   630	 *
   631	 *	int driver_gpuva_remap(struct drm_gpuva_op *op, void *__ctx)
   632	 *	{
   633	 *		struct driver_context *ctx = __ctx;
   634	 *		struct drm_gpuva *va = op->remap.unmap->va;
   635	 *
   636	 *		drm_gpuva_remap(ctx->prev_va, ctx->next_va, &op->remap);
   637	 *
   638	 *		if (op->remap.prev) {
   639	 *			drm_gpuva_link(ctx->prev_va, va->vm_bo);
   640	 *			ctx->prev_va = NULL;
   641	 *		}
   642	 *
   643	 *		if (op->remap.next) {
   644	 *			drm_gpuva_link(ctx->next_va, va->vm_bo);
   645	 *			ctx->next_va = NULL;
   646	 *		}
   647	 *
   648	 *		drm_gpuva_unlink(va);
   649	 *		kfree(va);
   650	 *
   651	 *		return 0;
   652	 *	}
   653	 *
   654	 *	int driver_gpuva_unmap(struct drm_gpuva_op *op, void *__ctx)
   655	 *	{
   656	 *		drm_gpuva_unlink(op->unmap.va);
   657	 *		drm_gpuva_unmap(&op->unmap);
   658	 *		kfree(op->unmap.va);
   659	 *
   660	 *		return 0;
   661	 *	}
   662	 */
   663	
   664	/**
   665	 * get_next_vm_bo_from_list() - get the next vm_bo element
   666	 * @__gpuvm: The GPU VM
   667	 * @__list_name: The name of the list we're iterating on
   668	 * @__local_list: A pointer to the local list used to store already iterated items
   669	 * @__prev_vm_bo: The previous element we got from drm_gpuvm_get_next_cached_vm_bo()
   670	 *
   671	 * This helper is here to provide lockless list iteration. Lockless as in, the
   672	 * iterator releases the lock immediately after picking the first element from
   673	 * the list, so list insertion deletion can happen concurrently.
   674	 *
   675	 * Elements popped from the original list are kept in a local list, so removal
   676	 * and is_empty checks can still happen while we're iterating the list.
   677	 */
   678	#define get_next_vm_bo_from_list(__gpuvm, __list_name, __local_list, __prev_vm_bo)	\
   679		({										\
   680			struct drm_gpuvm_bo *__vm_bo;						\
   681												\
   682			drm_gpuvm_bo_put(__prev_vm_bo);						\
   683												\
   684			spin_lock(&(__gpuvm)->__list_name.lock);				\
   685			while (!list_empty(&(__gpuvm)->__list_name.list)) {			\
   686				__vm_bo = list_first_entry(&(__gpuvm)->__list_name.list,	\
   687							   struct drm_gpuvm_bo,			\
   688							   list.entry.__list_name);		\
   689				if (drm_gpuvm_bo_get_unless_zero(__vm_bo)) {			\
   690					list_move_tail(&(__vm_bo)->list.entry.__list_name,	\
   691						       __local_list);				\
   692					break;							\
   693				} else {							\
   694					list_del_init(&(__vm_bo)->list.entry.__list_name);	\
   695					__vm_bo = NULL;						\
   696				}								\
   697			}									\
   698			spin_unlock(&(__gpuvm)->__list_name.lock);				\
   699												\
   700			__vm_bo;								\
   701		})
   702	
   703	/**
   704	 * for_each_vm_bo_in_list() - internal vm_bo list iterator
   705	 *
   706	 * This helper is here to provide lockless list iteration. Lockless as in, the
   707	 * iterator releases the lock immediately after picking the first element from the
   708	 * list, so list insertion and deletion can happen concurrently.
   709	 *
   710	 * Typical use:
   711	 *
   712	 *	struct drm_gpuvm_bo *vm_bo;
   713	 *	LIST_HEAD(my_local_list);
   714	 *
   715	 *	ret = 0;
   716	 *	drm_gpuvm_for_each_vm_bo(gpuvm, <list_name>, &my_local_list, vm_bo) {
   717	 *		ret = do_something_with_vm_bo(..., vm_bo);
   718	 *		if (ret)
   719	 *			break;
   720	 *	}
   721	 *	drm_gpuvm_bo_put(vm_bo);
   722	 *	drm_gpuvm_restore_vm_bo_list(gpuvm, <list_name>, &my_local_list);
   723	 *
   724	 *
   725	 * Only used for internal list iterations, not meant to be exposed to the outside
   726	 * world.
   727	 */
   728	#define for_each_vm_bo_in_list(__gpuvm, __list_name, __local_list, __vm_bo)	\
   729		for (__vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name,		\
   730							__local_list, NULL);		\
   731		     __vm_bo;								\
   732		     __vm_bo = get_next_vm_bo_from_list(__gpuvm, __list_name,		\
   733							__local_list, __vm_bo))		\
 > 734	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


More information about the dri-devel mailing list