aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/xe/xe_drm_client.c
blob: 82d1305e831f298f013338e4f7ee9e6e2ea67168 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// SPDX-License-Identifier: MIT
/*
 * Copyright © 2023 Intel Corporation
 */

#include <drm/drm_print.h>
#include <drm/xe_drm.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/types.h>

#include "xe_bo.h"
#include "xe_bo_types.h"
#include "xe_device_types.h"
#include "xe_drm_client.h"
#include "xe_trace.h"

/**
 * xe_drm_client_alloc() - Allocate drm client
 * @void: No arg
 *
 * Allocate drm client struct to track client memory against
 * same till client life. Call this API whenever new client
 * has opened xe device.
 *
 * Return: pointer to client struct or NULL if can't allocate
 */
struct xe_drm_client *xe_drm_client_alloc(void)
{
	struct xe_drm_client *client;

	client = kzalloc(sizeof(*client), GFP_KERNEL);
	if (!client)
		return NULL;

	kref_init(&client->kref);

#ifdef CONFIG_PROC_FS
	spin_lock_init(&client->bos_lock);
	INIT_LIST_HEAD(&client->bos_list);
#endif
	return client;
}

/**
 * __xe_drm_client_free() - Free client struct
 * @kref: The reference
 *
 * This frees client struct. Call this API when xe device is closed
 * by drm client.
 *
 * Return: void
 */
void __xe_drm_client_free(struct kref *kref)
{
	struct xe_drm_client *client =
		container_of(kref, typeof(*client), kref);

	kfree(client);
}

#ifdef CONFIG_PROC_FS
/**
 * xe_drm_client_add_bo() - Add BO for tracking client mem usage
 * @client: The drm client ptr
 * @bo: The xe BO ptr
 *
 * Add all BO created by individual drm client by calling this function.
 * This helps in tracking client memory usage.
 *
 * Return: void
 */
void xe_drm_client_add_bo(struct xe_drm_client *client,
			  struct xe_bo *bo)
{
	XE_WARN_ON(bo->client);
	XE_WARN_ON(!list_empty(&bo->client_link));

	spin_lock(&client->bos_lock);
	bo->client = xe_drm_client_get(client);
	list_add_tail_rcu(&bo->client_link, &client->bos_list);
	spin_unlock(&client->bos_lock);
}

/**
 * xe_drm_client_remove_bo() - Remove BO for tracking client mem usage
 * @bo: The xe BO ptr
 *
 * Remove all BO removed by individual drm client by calling this function.
 * This helps in tracking client memory usage.
 *
 * Return: void
 */
void xe_drm_client_remove_bo(struct xe_bo *bo)
{
	struct xe_drm_client *client = bo->client;

	spin_lock(&client->bos_lock);
	list_del_rcu(&bo->client_link);
	spin_unlock(&client->bos_lock);

	xe_drm_client_put(client);
}

static void bo_meminfo(struct xe_bo *bo,
		       struct drm_memory_stats stats[TTM_NUM_MEM_TYPES])
{
	u64 sz = bo->size;
	u32 mem_type;

	if (bo->placement.placement)
		mem_type = bo->placement.placement->mem_type;
	else
		mem_type = XE_PL_TT;

	if (bo->ttm.base.handle_count > 1)
		stats[mem_type].shared += sz;
	else
		stats[mem_type].private += sz;

	if (xe_bo_has_pages(bo)) {
		stats[mem_type].resident += sz;

		if (!dma_resv_test_signaled(bo->ttm.base.resv,
					    DMA_RESV_USAGE_BOOKKEEP))
			stats[mem_type].active += sz;
		else if (mem_type == XE_PL_SYSTEM)
			stats[mem_type].purgeable += sz;
	}
}

static void show_meminfo(struct drm_printer *p, struct drm_file *file)
{
	static const char *const mem_type_to_name[TTM_NUM_MEM_TYPES]  = {
		[XE_PL_SYSTEM] = "system",
		[XE_PL_TT] = "gtt",
		[XE_PL_VRAM0] = "vram0",
		[XE_PL_VRAM1] = "vram1",
		[4 ... 6] = NULL,
		[XE_PL_STOLEN] = "stolen"
	};
	struct drm_memory_stats stats[TTM_NUM_MEM_TYPES] = {};
	struct xe_file *xef = file->driver_priv;
	struct ttm_device *bdev = &xef->xe->ttm;
	struct ttm_resource_manager *man;
	struct xe_drm_client *client;
	struct drm_gem_object *obj;
	struct xe_bo *bo;
	unsigned int id;
	u32 mem_type;

	client = xef->client;

	/* Public objects. */
	spin_lock(&file->table_lock);
	idr_for_each_entry(&file->object_idr, obj, id) {
		struct xe_bo *bo = gem_to_xe_bo(obj);

		bo_meminfo(bo, stats);
	}
	spin_unlock(&file->table_lock);

	/* Internal objects. */
	spin_lock(&client->bos_lock);
	list_for_each_entry_rcu(bo, &client->bos_list, client_link) {
		if (!bo || !kref_get_unless_zero(&bo->ttm.base.refcount))
			continue;
		bo_meminfo(bo, stats);
		xe_bo_put(bo);
	}
	spin_unlock(&client->bos_lock);

	for (mem_type = XE_PL_SYSTEM; mem_type < TTM_NUM_MEM_TYPES; ++mem_type) {
		if (!mem_type_to_name[mem_type])
			continue;

		man = ttm_manager_type(bdev, mem_type);

		if (man) {
			drm_print_memory_stats(p,
					       &stats[mem_type],
					       DRM_GEM_OBJECT_RESIDENT |
					       (mem_type != XE_PL_SYSTEM ? 0 :
					       DRM_GEM_OBJECT_PURGEABLE),
					       mem_type_to_name[mem_type]);
		}
	}
}

/**
 * xe_drm_client_fdinfo() - Callback for fdinfo interface
 * @p: The drm_printer ptr
 * @file: The drm_file ptr
 *
 * This is callabck for drm fdinfo interface. Register this callback
 * in drm driver ops for show_fdinfo.
 *
 * Return: void
 */
void xe_drm_client_fdinfo(struct drm_printer *p, struct drm_file *file)
{
	show_meminfo(p, file);
}
#endif