xref: /linux/drivers/gpu/drm/xe/tests/xe_guc_g2g_test.c (revision 53597deca0e38c30e6cd4ba2114fa42d2bcd85bb)
1 // SPDX-License-Identifier: GPL-2.0 AND MIT
2 /*
3  * Copyright © 2025 Intel Corporation
4  */
5 
6 #include <linux/delay.h>
7 
8 #include <kunit/test.h>
9 #include <kunit/visibility.h>
10 
11 #include "tests/xe_kunit_helpers.h"
12 #include "tests/xe_pci_test.h"
13 #include "tests/xe_test.h"
14 
15 #include "xe_bo.h"
16 #include "xe_device.h"
17 #include "xe_pm.h"
18 
19 /*
20  * There are different ways to allocate the G2G buffers. The plan for this test
21  * is to make sure that all the possible options work. The particular option
22  * chosen by the driver may vary from one platform to another, it may also change
23  * with time. So to ensure consistency of testing, the relevant driver code is
24  * replicated here to guarantee it won't change without the test being updated
25  * to keep testing the other options.
26  *
27  * In order to test the actual code being used by the driver, there is also the
28  * 'default' scheme. That will use the official driver routines to test whatever
29  * method the driver is using on the current platform at the current time.
30  */
31 enum {
32 	/* Driver defined allocation scheme */
33 	G2G_CTB_TYPE_DEFAULT,
34 	/* Single buffer in host memory */
35 	G2G_CTB_TYPE_HOST,
36 	/* Single buffer in a specific tile, loops across all tiles */
37 	G2G_CTB_TYPE_TILE,
38 };
39 
40 /*
41  * Payload is opaque to GuC. So KMD can define any structure or size it wants.
42  */
43 struct g2g_test_payload  {
44 	u32 tx_dev;
45 	u32 tx_tile;
46 	u32 rx_dev;
47 	u32 rx_tile;
48 	u32 seqno;
49 };
50 
51 static int slot_index_from_gts(struct xe_gt *tx_gt, struct xe_gt *rx_gt)
52 {
53 	struct xe_device *xe = gt_to_xe(tx_gt);
54 	int idx = 0, found = 0, id, tx_idx, rx_idx;
55 	struct xe_gt *gt;
56 	struct kunit *test = kunit_get_current_test();
57 
58 	for (id = 0; id < xe->info.tile_count * xe->info.max_gt_per_tile; id++) {
59 		gt = xe_device_get_gt(xe, id);
60 		if (!gt)
61 			continue;
62 		if (gt == tx_gt) {
63 			tx_idx = idx;
64 			found++;
65 		}
66 		if (gt == rx_gt) {
67 			rx_idx = idx;
68 			found++;
69 		}
70 
71 		if (found == 2)
72 			break;
73 
74 		idx++;
75 	}
76 
77 	if (found != 2)
78 		KUNIT_FAIL(test, "GT index not found");
79 
80 	return (tx_idx * xe->info.gt_count) + rx_idx;
81 }
82 
83 static void g2g_test_send(struct kunit *test, struct xe_guc *guc,
84 			  u32 far_tile, u32 far_dev,
85 			  struct g2g_test_payload *payload)
86 {
87 	struct xe_device *xe = guc_to_xe(guc);
88 	struct xe_gt *gt = guc_to_gt(guc);
89 	u32 *action, total;
90 	size_t payload_len;
91 	int ret;
92 
93 	static_assert(IS_ALIGNED(sizeof(*payload), sizeof(u32)));
94 	payload_len = sizeof(*payload) / sizeof(u32);
95 
96 	total = 4 + payload_len;
97 	action = kunit_kmalloc_array(test, total, sizeof(*action), GFP_KERNEL);
98 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, action);
99 
100 	action[0] = XE_GUC_ACTION_TEST_G2G_SEND;
101 	action[1] = far_tile;
102 	action[2] = far_dev;
103 	action[3] = payload_len;
104 	memcpy(action + 4, payload, payload_len * sizeof(u32));
105 
106 	atomic_inc(&xe->g2g_test_count);
107 
108 	/*
109 	 * Should specify the expected response notification here. Problem is that
110 	 * the response will be coming from a different GuC. By the end, it should
111 	 * all add up as long as an equal number of messages are sent from each GuC
112 	 * and to each GuC. However, in the middle negative reservation space errors
113 	 * and such like can occur. Rather than add intrusive changes to the CT layer
114 	 * it is simpler to just not bother counting it at all. The system should be
115 	 * idle when running the selftest, and the selftest's notification total size
116 	 * is well within the G2H allocation size. So there should be no issues with
117 	 * needing to block for space, which is all the tracking code is really for.
118 	 */
119 	ret = xe_guc_ct_send(&guc->ct, action, total, 0, 0);
120 	kunit_kfree(test, action);
121 	KUNIT_ASSERT_EQ_MSG(test, 0, ret, "G2G send failed: %d [%d:%d -> %d:%d]\n", ret,
122 			    gt_to_tile(gt)->id, G2G_DEV(gt), far_tile, far_dev);
123 }
124 
125 /*
126  * NB: Can't use KUNIT_ASSERT and friends in here as this is called asynchronously
127  * from the G2H notification handler. Need that to actually complete rather than
128  * thread-abort in order to keep the rest of the driver alive!
129  */
130 int xe_guc_g2g_test_notification(struct xe_guc *guc, u32 *msg, u32 len)
131 {
132 	struct xe_device *xe = guc_to_xe(guc);
133 	struct xe_gt *rx_gt = guc_to_gt(guc), *test_gt, *tx_gt = NULL;
134 	u32 tx_tile, tx_dev, rx_tile, rx_dev, idx, got_len;
135 	struct g2g_test_payload *payload;
136 	size_t payload_len;
137 	int ret = 0, i;
138 
139 	payload_len = sizeof(*payload) / sizeof(u32);
140 
141 	if (unlikely(len != (G2H_LEN_DW_G2G_NOTIFY_MIN + payload_len))) {
142 		xe_gt_err(rx_gt, "G2G test notification invalid length %u", len);
143 		ret = -EPROTO;
144 		goto done;
145 	}
146 
147 	tx_tile = msg[0];
148 	tx_dev = msg[1];
149 	got_len = msg[2];
150 	payload = (struct g2g_test_payload *)(msg + 3);
151 
152 	rx_tile = gt_to_tile(rx_gt)->id;
153 	rx_dev = G2G_DEV(rx_gt);
154 
155 	if (got_len != payload_len) {
156 		xe_gt_err(rx_gt, "G2G: Invalid payload length: %u vs %zu\n", got_len, payload_len);
157 		ret = -EPROTO;
158 		goto done;
159 	}
160 
161 	if (payload->tx_dev != tx_dev || payload->tx_tile != tx_tile ||
162 	    payload->rx_dev != rx_dev || payload->rx_tile != rx_tile) {
163 		xe_gt_err(rx_gt, "G2G: Invalid payload: %d:%d -> %d:%d vs %d:%d -> %d:%d! [%d]\n",
164 			  payload->tx_tile, payload->tx_dev, payload->rx_tile, payload->rx_dev,
165 			  tx_tile, tx_dev, rx_tile, rx_dev, payload->seqno);
166 		ret = -EPROTO;
167 		goto done;
168 	}
169 
170 	if (!xe->g2g_test_array) {
171 		xe_gt_err(rx_gt, "G2G: Missing test array!\n");
172 		ret = -ENOMEM;
173 		goto done;
174 	}
175 
176 	for_each_gt(test_gt, xe, i) {
177 		if (gt_to_tile(test_gt)->id != tx_tile)
178 			continue;
179 
180 		if (G2G_DEV(test_gt) != tx_dev)
181 			continue;
182 
183 		if (tx_gt) {
184 			xe_gt_err(rx_gt, "G2G: Got duplicate TX GTs: %d vs %d for %d:%d!\n",
185 				  tx_gt->info.id, test_gt->info.id, tx_tile, tx_dev);
186 			ret = -EINVAL;
187 			goto done;
188 		}
189 
190 		tx_gt = test_gt;
191 	}
192 	if (!tx_gt) {
193 		xe_gt_err(rx_gt, "G2G: Failed to find a TX GT for %d:%d!\n", tx_tile, tx_dev);
194 		ret = -EINVAL;
195 		goto done;
196 	}
197 
198 	idx = slot_index_from_gts(tx_gt, rx_gt);
199 
200 	if (xe->g2g_test_array[idx] != payload->seqno - 1) {
201 		xe_gt_err(rx_gt, "G2G: Seqno mismatch %d vs %d for %d:%d -> %d:%d!\n",
202 			  xe->g2g_test_array[idx], payload->seqno - 1,
203 			  tx_tile, tx_dev, rx_tile, rx_dev);
204 		ret = -EINVAL;
205 		goto done;
206 	}
207 
208 	xe->g2g_test_array[idx] = payload->seqno;
209 
210 done:
211 	atomic_dec(&xe->g2g_test_count);
212 	return ret;
213 }
214 
215 #define G2G_WAIT_TIMEOUT_MS 100
216 #define G2G_WAIT_POLL_MS 1
217 
218 /*
219  * Send the given seqno from all GuCs to all other GuCs in tile/GT order
220  */
221 static void g2g_test_in_order(struct kunit *test, struct xe_device *xe, u32 seqno)
222 {
223 	struct xe_gt *near_gt, *far_gt;
224 	int i, j, waited;
225 	u32 idx;
226 
227 	for_each_gt(near_gt, xe, i) {
228 		u32 near_tile = gt_to_tile(near_gt)->id;
229 		u32 near_dev = G2G_DEV(near_gt);
230 
231 		for_each_gt(far_gt, xe, j) {
232 			u32 far_tile = gt_to_tile(far_gt)->id;
233 			u32 far_dev = G2G_DEV(far_gt);
234 			struct g2g_test_payload payload;
235 
236 			if (far_gt->info.id == near_gt->info.id)
237 				continue;
238 
239 			payload.tx_dev = near_dev;
240 			payload.tx_tile = near_tile;
241 			payload.rx_dev = far_dev;
242 			payload.rx_tile = far_tile;
243 			payload.seqno = seqno;
244 
245 			/* Calculate idx for event-based wait */
246 			idx = slot_index_from_gts(near_gt, far_gt);
247 			waited = 0;
248 
249 			/*
250 			 * Wait for previous seqno to be acknowledged before sending,
251 			 * to avoid queuing too many back-to-back messages and
252 			 * causing a test timeout. Actual correctness of message
253 			 * will be checked later in xe_guc_g2g_test_notification()
254 			 */
255 			while (xe->g2g_test_array[idx] != (seqno - 1)) {
256 				msleep(G2G_WAIT_POLL_MS);
257 				waited += G2G_WAIT_POLL_MS;
258 				if (waited >= G2G_WAIT_TIMEOUT_MS) {
259 					kunit_info(test, "Timeout waiting! tx gt: %d, rx gt: %d\n",
260 						   near_gt->info.id, far_gt->info.id);
261 					break;
262 				}
263 			}
264 
265 			g2g_test_send(test, &near_gt->uc.guc, far_tile, far_dev, &payload);
266 		}
267 	}
268 }
269 
270 #define WAIT_TIME_MS	100
271 #define WAIT_COUNT	(1000 / WAIT_TIME_MS)
272 
273 static void g2g_wait_for_complete(void *_xe)
274 {
275 	struct xe_device *xe = (struct xe_device *)_xe;
276 	struct kunit *test = kunit_get_current_test();
277 	int wait = 0;
278 
279 	/* Wait for all G2H messages to be received */
280 	while (atomic_read(&xe->g2g_test_count)) {
281 		if (++wait > WAIT_COUNT)
282 			break;
283 
284 		msleep(WAIT_TIME_MS);
285 	}
286 
287 	KUNIT_ASSERT_EQ_MSG(test, 0, atomic_read(&xe->g2g_test_count),
288 			    "Timed out waiting for notifications\n");
289 	kunit_info(test, "Got all notifications back\n");
290 }
291 
292 #undef WAIT_TIME_MS
293 #undef WAIT_COUNT
294 
295 static void g2g_clean_array(void *_xe)
296 {
297 	struct xe_device *xe = (struct xe_device *)_xe;
298 
299 	xe->g2g_test_array = NULL;
300 }
301 
302 #define NUM_LOOPS	16
303 
304 static void g2g_run_test(struct kunit *test, struct xe_device *xe)
305 {
306 	u32 seqno, max_array;
307 	int ret, i, j;
308 
309 	max_array = xe->info.gt_count * xe->info.gt_count;
310 	xe->g2g_test_array = kunit_kcalloc(test, max_array, sizeof(u32), GFP_KERNEL);
311 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xe->g2g_test_array);
312 
313 	ret = kunit_add_action_or_reset(test, g2g_clean_array, xe);
314 	KUNIT_ASSERT_EQ_MSG(test, 0, ret, "Failed to register clean up action\n");
315 
316 	/*
317 	 * Send incrementing seqnos from all GuCs to all other GuCs in tile/GT order.
318 	 * Tile/GT order doesn't really mean anything to the hardware but it is going
319 	 * to be a fixed sequence every time.
320 	 *
321 	 * Verify that each one comes back having taken the correct route.
322 	 */
323 	ret = kunit_add_action(test, g2g_wait_for_complete, xe);
324 	KUNIT_ASSERT_EQ_MSG(test, 0, ret, "Failed to register clean up action\n");
325 	for (seqno = 1; seqno < NUM_LOOPS; seqno++)
326 		g2g_test_in_order(test, xe, seqno);
327 	seqno--;
328 
329 	kunit_release_action(test, &g2g_wait_for_complete, xe);
330 
331 	/* Check for the final seqno in each slot */
332 	for (i = 0; i < xe->info.gt_count; i++) {
333 		for (j = 0; j < xe->info.gt_count; j++) {
334 			u32 idx = (j * xe->info.gt_count) + i;
335 
336 			if (i == j)
337 				KUNIT_ASSERT_EQ_MSG(test, 0, xe->g2g_test_array[idx],
338 						    "identity seqno modified: %d for %dx%d!\n",
339 						    xe->g2g_test_array[idx], i, j);
340 			else
341 				KUNIT_ASSERT_EQ_MSG(test, seqno, xe->g2g_test_array[idx],
342 						    "invalid seqno: %d vs %d for %dx%d!\n",
343 						    xe->g2g_test_array[idx], seqno, i, j);
344 		}
345 	}
346 
347 	kunit_kfree(test, xe->g2g_test_array);
348 	kunit_release_action(test, &g2g_clean_array, xe);
349 
350 	kunit_info(test, "Test passed\n");
351 }
352 
353 #undef NUM_LOOPS
354 
355 static void g2g_ct_stop(struct xe_guc *guc)
356 {
357 	struct xe_gt *remote_gt, *gt = guc_to_gt(guc);
358 	struct xe_device *xe = gt_to_xe(gt);
359 	int i, t;
360 
361 	for_each_gt(remote_gt, xe, i) {
362 		u32 tile, dev;
363 
364 		if (remote_gt->info.id == gt->info.id)
365 			continue;
366 
367 		tile = gt_to_tile(remote_gt)->id;
368 		dev = G2G_DEV(remote_gt);
369 
370 		for (t = 0; t < XE_G2G_TYPE_LIMIT; t++)
371 			guc_g2g_deregister(guc, tile, dev, t);
372 	}
373 }
374 
375 /* Size of a single allocation that contains all G2G CTBs across all GTs */
376 static u32 g2g_ctb_size(struct kunit *test, struct xe_device *xe)
377 {
378 	unsigned int count = xe->info.gt_count;
379 	u32 num_channels = (count * (count - 1)) / 2;
380 
381 	kunit_info(test, "Size: (%d * %d / 2) * %d * 0x%08X + 0x%08X => 0x%08X [%d]\n",
382 		   count, count - 1, XE_G2G_TYPE_LIMIT, G2G_BUFFER_SIZE, G2G_DESC_AREA_SIZE,
383 		   num_channels * XE_G2G_TYPE_LIMIT * G2G_BUFFER_SIZE + G2G_DESC_AREA_SIZE,
384 		   num_channels * XE_G2G_TYPE_LIMIT);
385 
386 	return num_channels * XE_G2G_TYPE_LIMIT * G2G_BUFFER_SIZE + G2G_DESC_AREA_SIZE;
387 }
388 
389 /*
390  * Use the driver's regular CTB allocation scheme.
391  */
392 static void g2g_alloc_default(struct kunit *test, struct xe_device *xe)
393 {
394 	struct xe_gt *gt;
395 	int i;
396 
397 	kunit_info(test, "Default [tiles = %d, GTs = %d]\n",
398 		   xe->info.tile_count, xe->info.gt_count);
399 
400 	for_each_gt(gt, xe, i) {
401 		struct xe_guc *guc = &gt->uc.guc;
402 		int ret;
403 
404 		ret = guc_g2g_alloc(guc);
405 		KUNIT_ASSERT_EQ_MSG(test, 0, ret, "G2G alloc failed: %pe", ERR_PTR(ret));
406 		continue;
407 	}
408 }
409 
410 static void g2g_distribute(struct kunit *test, struct xe_device *xe, struct xe_bo *bo)
411 {
412 	struct xe_gt *root_gt, *gt;
413 	int i;
414 
415 	root_gt = xe_device_get_gt(xe, 0);
416 	root_gt->uc.guc.g2g.bo = bo;
417 	root_gt->uc.guc.g2g.owned = true;
418 	kunit_info(test, "[%d.%d] Assigned 0x%p\n", gt_to_tile(root_gt)->id, root_gt->info.id, bo);
419 
420 	for_each_gt(gt, xe, i) {
421 		if (gt->info.id != 0) {
422 			gt->uc.guc.g2g.owned = false;
423 			gt->uc.guc.g2g.bo = xe_bo_get(bo);
424 			kunit_info(test, "[%d.%d] Pinned 0x%p\n",
425 				   gt_to_tile(gt)->id, gt->info.id, gt->uc.guc.g2g.bo);
426 		}
427 
428 		KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gt->uc.guc.g2g.bo);
429 	}
430 }
431 
432 /*
433  * Allocate a single blob on the host and split between all G2G CTBs.
434  */
435 static void g2g_alloc_host(struct kunit *test, struct xe_device *xe)
436 {
437 	struct xe_bo *bo;
438 	u32 g2g_size;
439 
440 	kunit_info(test, "Host [tiles = %d, GTs = %d]\n", xe->info.tile_count, xe->info.gt_count);
441 
442 	g2g_size = g2g_ctb_size(test, xe);
443 	bo = xe_managed_bo_create_pin_map(xe, xe_device_get_root_tile(xe), g2g_size,
444 					  XE_BO_FLAG_SYSTEM |
445 					  XE_BO_FLAG_GGTT |
446 					  XE_BO_FLAG_GGTT_ALL |
447 					  XE_BO_FLAG_GGTT_INVALIDATE);
448 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bo);
449 	kunit_info(test, "[HST] G2G buffer create: 0x%p\n", bo);
450 
451 	xe_map_memset(xe, &bo->vmap, 0, 0, g2g_size);
452 
453 	g2g_distribute(test, xe, bo);
454 }
455 
456 /*
457  * Allocate a single blob on the given tile and split between all G2G CTBs.
458  */
459 static void g2g_alloc_tile(struct kunit *test, struct xe_device *xe, struct xe_tile *tile)
460 {
461 	struct xe_bo *bo;
462 	u32 g2g_size;
463 
464 	KUNIT_ASSERT_TRUE(test, IS_DGFX(xe));
465 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, tile);
466 
467 	kunit_info(test, "Tile %d [tiles = %d, GTs = %d]\n",
468 		   tile->id, xe->info.tile_count, xe->info.gt_count);
469 
470 	g2g_size = g2g_ctb_size(test, xe);
471 	bo = xe_managed_bo_create_pin_map(xe, tile, g2g_size,
472 					  XE_BO_FLAG_VRAM_IF_DGFX(tile) |
473 					  XE_BO_FLAG_GGTT |
474 					  XE_BO_FLAG_GGTT_ALL |
475 					  XE_BO_FLAG_GGTT_INVALIDATE);
476 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bo);
477 	kunit_info(test, "[%d.*] G2G buffer create: 0x%p\n", tile->id, bo);
478 
479 	xe_map_memset(xe, &bo->vmap, 0, 0, g2g_size);
480 
481 	g2g_distribute(test, xe, bo);
482 }
483 
484 static void g2g_free(struct kunit *test, struct xe_device *xe)
485 {
486 	struct xe_gt *gt;
487 	struct xe_bo *bo;
488 	int i;
489 
490 	for_each_gt(gt, xe, i) {
491 		bo = gt->uc.guc.g2g.bo;
492 		if (!bo)
493 			continue;
494 
495 		if (gt->uc.guc.g2g.owned) {
496 			xe_managed_bo_unpin_map_no_vm(bo);
497 			kunit_info(test, "[%d.%d] Unmapped 0x%p\n",
498 				   gt_to_tile(gt)->id, gt->info.id, bo);
499 		} else {
500 			xe_bo_put(bo);
501 			kunit_info(test, "[%d.%d] Unpinned 0x%p\n",
502 				   gt_to_tile(gt)->id, gt->info.id, bo);
503 		}
504 
505 		gt->uc.guc.g2g.bo = NULL;
506 	}
507 }
508 
509 static void g2g_stop(struct kunit *test, struct xe_device *xe)
510 {
511 	struct xe_gt *gt;
512 	int i;
513 
514 	for_each_gt(gt, xe, i) {
515 		struct xe_guc *guc = &gt->uc.guc;
516 
517 		if (!guc->g2g.bo)
518 			continue;
519 
520 		g2g_ct_stop(guc);
521 	}
522 
523 	g2g_free(test, xe);
524 }
525 
526 /*
527  * Generate a unique id for each bi-directional CTB for each pair of
528  * near and far tiles/devices. The id can then be used as an index into
529  * a single allocation that is sub-divided into multiple CTBs.
530  *
531  * For example, with two devices per tile and two tiles, the table should
532  * look like:
533  *           Far <tile>.<dev>
534  *         0.0   0.1   1.0   1.1
535  * N 0.0  --/-- 00/01 02/03 04/05
536  * e 0.1  01/00 --/-- 06/07 08/09
537  * a 1.0  03/02 07/06 --/-- 10/11
538  * r 1.1  05/04 09/08 11/10 --/--
539  *
540  * Where each entry is Rx/Tx channel id.
541  *
542  * So GuC #3 (tile 1, dev 1) talking to GuC #2 (tile 1, dev 0) would
543  * be reading from channel #11 and writing to channel #10. Whereas,
544  * GuC #2 talking to GuC #3 would be read on #10 and write to #11.
545  */
546 static int g2g_slot_flat(u32 near_tile, u32 near_dev, u32 far_tile, u32 far_dev,
547 			 u32 type, u32 max_inst, bool have_dev)
548 {
549 	u32 near = near_tile, far = far_tile;
550 	u32 idx = 0, x, y, direction;
551 	int i;
552 
553 	if (have_dev) {
554 		near = (near << 1) | near_dev;
555 		far = (far << 1) | far_dev;
556 	}
557 
558 	/* No need to send to one's self */
559 	if (far == near)
560 		return -1;
561 
562 	if (far > near) {
563 		/* Top right table half */
564 		x = far;
565 		y = near;
566 
567 		/* T/R is 'forwards' direction */
568 		direction = type;
569 	} else {
570 		/* Bottom left table half */
571 		x = near;
572 		y = far;
573 
574 		/* B/L is 'backwards' direction */
575 		direction = (1 - type);
576 	}
577 
578 	/* Count the rows prior to the target */
579 	for (i = y; i > 0; i--)
580 		idx += max_inst - i;
581 
582 	/* Count this row up to the target */
583 	idx += (x - 1 - y);
584 
585 	/* Slots are in Rx/Tx pairs */
586 	idx *= 2;
587 
588 	/* Pick Rx/Tx direction */
589 	idx += direction;
590 
591 	return idx;
592 }
593 
594 static int g2g_register_flat(struct xe_guc *guc, u32 far_tile, u32 far_dev, u32 type, bool have_dev)
595 {
596 	struct xe_gt *gt = guc_to_gt(guc);
597 	struct xe_device *xe = gt_to_xe(gt);
598 	u32 near_tile = gt_to_tile(gt)->id;
599 	u32 near_dev = G2G_DEV(gt);
600 	u32 max = xe->info.gt_count;
601 	int idx;
602 	u32 base, desc, buf;
603 
604 	if (!guc->g2g.bo)
605 		return -ENODEV;
606 
607 	idx = g2g_slot_flat(near_tile, near_dev, far_tile, far_dev, type, max, have_dev);
608 	xe_assert(xe, idx >= 0);
609 
610 	base = guc_bo_ggtt_addr(guc, guc->g2g.bo);
611 	desc = base + idx * G2G_DESC_SIZE;
612 	buf = base + idx * G2G_BUFFER_SIZE + G2G_DESC_AREA_SIZE;
613 
614 	xe_assert(xe, (desc - base + G2G_DESC_SIZE) <= G2G_DESC_AREA_SIZE);
615 	xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= xe_bo_size(guc->g2g.bo));
616 
617 	return guc_action_register_g2g_buffer(guc, type, far_tile, far_dev,
618 					      desc, buf, G2G_BUFFER_SIZE);
619 }
620 
621 static void g2g_start(struct kunit *test, struct xe_guc *guc)
622 {
623 	struct xe_gt *remote_gt, *gt = guc_to_gt(guc);
624 	struct xe_device *xe = gt_to_xe(gt);
625 	unsigned int i;
626 	int t, ret;
627 	bool have_dev;
628 
629 	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, guc->g2g.bo);
630 
631 	/* GuC interface will need extending if more GT device types are ever created. */
632 	KUNIT_ASSERT_TRUE(test,
633 			  (gt->info.type == XE_GT_TYPE_MAIN) ||
634 			  (gt->info.type == XE_GT_TYPE_MEDIA));
635 
636 	/* Channel numbering depends on whether there are multiple GTs per tile */
637 	have_dev = xe->info.gt_count > xe->info.tile_count;
638 
639 	for_each_gt(remote_gt, xe, i) {
640 		u32 tile, dev;
641 
642 		if (remote_gt->info.id == gt->info.id)
643 			continue;
644 
645 		tile = gt_to_tile(remote_gt)->id;
646 		dev = G2G_DEV(remote_gt);
647 
648 		for (t = 0; t < XE_G2G_TYPE_LIMIT; t++) {
649 			ret = g2g_register_flat(guc, tile, dev, t, have_dev);
650 			KUNIT_ASSERT_EQ_MSG(test, 0, ret, "G2G register failed: %pe", ERR_PTR(ret));
651 		}
652 	}
653 }
654 
655 static void g2g_reinit(struct kunit *test, struct xe_device *xe, int ctb_type, struct xe_tile *tile)
656 {
657 	struct xe_gt *gt;
658 	int i, found = 0;
659 
660 	g2g_stop(test, xe);
661 
662 	for_each_gt(gt, xe, i) {
663 		struct xe_guc *guc = &gt->uc.guc;
664 
665 		KUNIT_ASSERT_NULL(test, guc->g2g.bo);
666 	}
667 
668 	switch (ctb_type) {
669 	case G2G_CTB_TYPE_DEFAULT:
670 		g2g_alloc_default(test, xe);
671 		break;
672 
673 	case G2G_CTB_TYPE_HOST:
674 		g2g_alloc_host(test, xe);
675 		break;
676 
677 	case G2G_CTB_TYPE_TILE:
678 		g2g_alloc_tile(test, xe, tile);
679 		break;
680 
681 	default:
682 		KUNIT_ASSERT_TRUE(test, false);
683 	}
684 
685 	for_each_gt(gt, xe, i) {
686 		struct xe_guc *guc = &gt->uc.guc;
687 
688 		if (!guc->g2g.bo)
689 			continue;
690 
691 		if (ctb_type == G2G_CTB_TYPE_DEFAULT)
692 			guc_g2g_start(guc);
693 		else
694 			g2g_start(test, guc);
695 		found++;
696 	}
697 
698 	KUNIT_ASSERT_GT_MSG(test, found, 1, "insufficient G2G channels running: %d", found);
699 
700 	kunit_info(test, "Testing across %d GTs\n", found);
701 }
702 
703 static void g2g_recreate_ctb(void *_xe)
704 {
705 	struct xe_device *xe = (struct xe_device *)_xe;
706 	struct kunit *test = kunit_get_current_test();
707 
708 	g2g_stop(test, xe);
709 
710 	if (xe_guc_g2g_wanted(xe))
711 		g2g_reinit(test, xe, G2G_CTB_TYPE_DEFAULT, NULL);
712 }
713 
714 static void g2g_pm_runtime_put(void *_xe)
715 {
716 	struct xe_device *xe = (struct xe_device *)_xe;
717 
718 	xe_pm_runtime_put(xe);
719 }
720 
721 static void g2g_pm_runtime_get(struct kunit *test)
722 {
723 	struct xe_device *xe = test->priv;
724 	int ret;
725 
726 	xe_pm_runtime_get(xe);
727 	ret = kunit_add_action_or_reset(test, g2g_pm_runtime_put, xe);
728 	KUNIT_ASSERT_EQ_MSG(test, 0, ret, "Failed to register runtime PM action\n");
729 }
730 
731 static void g2g_check_skip(struct kunit *test)
732 {
733 	struct xe_device *xe = test->priv;
734 	struct xe_gt *gt;
735 	int i;
736 
737 	if (IS_SRIOV_VF(xe))
738 		kunit_skip(test, "not supported from a VF");
739 
740 	if (xe->info.gt_count <= 1)
741 		kunit_skip(test, "not enough GTs");
742 
743 	for_each_gt(gt, xe, i) {
744 		struct xe_guc *guc = &gt->uc.guc;
745 
746 		if (guc->fw.build_type == CSS_UKERNEL_INFO_BUILDTYPE_PROD)
747 			kunit_skip(test,
748 				   "G2G test interface not available in production firmware builds\n");
749 	}
750 }
751 
752 /*
753  * Simple test that does not try to recreate the CTBs.
754  * Requires that the platform already enables G2G comms
755  * but has no risk of leaving the system in a broken state
756  * afterwards.
757  */
758 static void xe_live_guc_g2g_kunit_default(struct kunit *test)
759 {
760 	struct xe_device *xe = test->priv;
761 
762 	if (!xe_guc_g2g_wanted(xe))
763 		kunit_skip(test, "G2G not enabled");
764 
765 	g2g_check_skip(test);
766 
767 	g2g_pm_runtime_get(test);
768 
769 	kunit_info(test, "Testing default CTBs\n");
770 	g2g_run_test(test, xe);
771 
772 	kunit_release_action(test, &g2g_pm_runtime_put, xe);
773 }
774 
775 /*
776  * More complex test that re-creates the CTBs in various location to
777  * test access to each location from each GuC. Can be run even on
778  * systems that do not enable G2G by default. On the other hand,
779  * because it recreates the CTBs, if something goes wrong it could
780  * leave the system with broken G2G comms.
781  */
782 static void xe_live_guc_g2g_kunit_allmem(struct kunit *test)
783 {
784 	struct xe_device *xe = test->priv;
785 	int ret;
786 
787 	g2g_check_skip(test);
788 
789 	g2g_pm_runtime_get(test);
790 
791 	/* Make sure to leave the system as we found it */
792 	ret = kunit_add_action_or_reset(test, g2g_recreate_ctb, xe);
793 	KUNIT_ASSERT_EQ_MSG(test, 0, ret, "Failed to register CTB re-creation action\n");
794 
795 	kunit_info(test, "Testing CTB type 'default'...\n");
796 	g2g_reinit(test, xe, G2G_CTB_TYPE_DEFAULT, NULL);
797 	g2g_run_test(test, xe);
798 
799 	kunit_info(test, "Testing CTB type 'host'...\n");
800 	g2g_reinit(test, xe, G2G_CTB_TYPE_HOST, NULL);
801 	g2g_run_test(test, xe);
802 
803 	if (IS_DGFX(xe)) {
804 		struct xe_tile *tile;
805 		int id;
806 
807 		for_each_tile(tile, xe, id) {
808 			kunit_info(test, "Testing CTB type 'tile: #%d'...\n", id);
809 
810 			g2g_reinit(test, xe, G2G_CTB_TYPE_TILE, tile);
811 			g2g_run_test(test, xe);
812 		}
813 	} else {
814 		kunit_info(test, "Skipping local memory on integrated platform\n");
815 	}
816 
817 	kunit_release_action(test, g2g_recreate_ctb, xe);
818 	kunit_release_action(test, g2g_pm_runtime_put, xe);
819 }
820 
821 static struct kunit_case xe_guc_g2g_tests[] = {
822 	KUNIT_CASE_PARAM(xe_live_guc_g2g_kunit_default, xe_pci_live_device_gen_param),
823 	KUNIT_CASE_PARAM(xe_live_guc_g2g_kunit_allmem, xe_pci_live_device_gen_param),
824 	{}
825 };
826 
827 VISIBLE_IF_KUNIT
828 struct kunit_suite xe_guc_g2g_test_suite = {
829 	.name = "xe_guc_g2g",
830 	.test_cases = xe_guc_g2g_tests,
831 	.init = xe_kunit_helper_xe_device_live_test_init,
832 };
833 EXPORT_SYMBOL_IF_KUNIT(xe_guc_g2g_test_suite);
834