drm_sysctl.c revision 183833
1/*-
2 * Copyright 2003 Eric Anholt
3 * All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * ERIC ANHOLT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <sys/cdefs.h>
25__FBSDID("$FreeBSD: head/sys/dev/drm/drm_sysctl.c 183833 2008-10-13 18:03:27Z rnoland $");
26
27/** @file drm_sysctl.c
28 * Implementation of various sysctls for controlling DRM behavior and reporting
29 * debug information.
30 */
31
32#include "dev/drm/drmP.h"
33#include "dev/drm/drm.h"
34
35#include <sys/sysctl.h>
36
37static int	   drm_name_info DRM_SYSCTL_HANDLER_ARGS;
38static int	   drm_vm_info DRM_SYSCTL_HANDLER_ARGS;
39static int	   drm_clients_info DRM_SYSCTL_HANDLER_ARGS;
40static int	   drm_bufs_info DRM_SYSCTL_HANDLER_ARGS;
41
42struct drm_sysctl_list {
43	const char *name;
44	int	   (*f) DRM_SYSCTL_HANDLER_ARGS;
45} drm_sysctl_list[] = {
46	{"name",    drm_name_info},
47	{"vm",	    drm_vm_info},
48	{"clients", drm_clients_info},
49	{"bufs",    drm_bufs_info},
50};
51#define DRM_SYSCTL_ENTRIES (sizeof(drm_sysctl_list)/sizeof(drm_sysctl_list[0]))
52
53struct drm_sysctl_info {
54	struct sysctl_ctx_list ctx;
55	char		       name[2];
56};
57
58int drm_sysctl_init(struct drm_device *dev)
59{
60	struct drm_sysctl_info *info;
61	struct sysctl_oid *oid;
62	struct sysctl_oid *top, *drioid;
63	int		  i;
64
65	info = malloc(sizeof *info, DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
66	if ( !info )
67		return 1;
68	dev->sysctl = info;
69
70	/* Add the sysctl node for DRI if it doesn't already exist */
71	drioid = SYSCTL_ADD_NODE( &info->ctx, &sysctl__hw_children, OID_AUTO, "dri", CTLFLAG_RW, NULL, "DRI Graphics");
72	if (!drioid)
73		return 1;
74
75	/* Find the next free slot under hw.dri */
76	i = 0;
77	SLIST_FOREACH(oid, SYSCTL_CHILDREN(drioid), oid_link) {
78		if (i <= oid->oid_arg2)
79			i = oid->oid_arg2 + 1;
80	}
81	if (i>9)
82		return 1;
83
84	/* Add the hw.dri.x for our device */
85	info->name[0] = '0' + i;
86	info->name[1] = 0;
87	top = SYSCTL_ADD_NODE( &info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, info->name, CTLFLAG_RW, NULL, NULL);
88	if (!top)
89		return 1;
90
91	for (i = 0; i < DRM_SYSCTL_ENTRIES; i++) {
92		oid = SYSCTL_ADD_OID(&info->ctx,
93			SYSCTL_CHILDREN(top),
94			OID_AUTO,
95			drm_sysctl_list[i].name,
96			CTLTYPE_INT | CTLFLAG_RD,
97			dev,
98			0,
99			drm_sysctl_list[i].f,
100			"A",
101			NULL);
102		if (!oid)
103			return 1;
104	}
105	SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(top), OID_AUTO, "debug",
106	    CTLFLAG_RW, &drm_debug_flag, sizeof(drm_debug_flag),
107	    "Enable debugging output");
108
109	return 0;
110}
111
112int drm_sysctl_cleanup(struct drm_device *dev)
113{
114	int error;
115	error = sysctl_ctx_free( &dev->sysctl->ctx );
116
117	free(dev->sysctl, DRM_MEM_DRIVER);
118	dev->sysctl = NULL;
119
120	return error;
121}
122
123#define DRM_SYSCTL_PRINT(fmt, arg...)				\
124do {								\
125	snprintf(buf, sizeof(buf), fmt, ##arg);			\
126	retcode = SYSCTL_OUT(req, buf, strlen(buf));		\
127	if (retcode)						\
128		goto done;					\
129} while (0)
130
131static int drm_name_info DRM_SYSCTL_HANDLER_ARGS
132{
133	struct drm_device *dev = arg1;
134	char buf[128];
135	int retcode;
136	int hasunique = 0;
137
138	DRM_SYSCTL_PRINT("%s 0x%x", dev->driver->name, dev2udev(dev->devnode));
139
140	DRM_LOCK();
141	if (dev->unique) {
142		snprintf(buf, sizeof(buf), " %s", dev->unique);
143		hasunique = 1;
144	}
145	DRM_UNLOCK();
146
147	if (hasunique)
148		SYSCTL_OUT(req, buf, strlen(buf));
149
150	SYSCTL_OUT(req, "", 1);
151
152done:
153	return retcode;
154}
155
156static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS
157{
158	struct drm_device *dev = arg1;
159	drm_local_map_t *map, *tempmaps;
160	const char   *types[] = { "FB", "REG", "SHM", "AGP", "SG" };
161	const char *type, *yesno;
162	int i, mapcount;
163	char buf[128];
164	int retcode;
165
166	/* We can't hold the lock while doing SYSCTL_OUTs, so allocate a
167	 * temporary copy of all the map entries and then SYSCTL_OUT that.
168	 */
169	DRM_LOCK();
170
171	mapcount = 0;
172	TAILQ_FOREACH(map, &dev->maplist, link)
173		mapcount++;
174
175	tempmaps = malloc(sizeof(drm_local_map_t) * mapcount, DRM_MEM_DRIVER,
176	    M_NOWAIT);
177	if (tempmaps == NULL) {
178		DRM_UNLOCK();
179		return ENOMEM;
180	}
181
182	i = 0;
183	TAILQ_FOREACH(map, &dev->maplist, link)
184		tempmaps[i++] = *map;
185
186	DRM_UNLOCK();
187
188	DRM_SYSCTL_PRINT("\nslot	 offset	      size type flags	 "
189			 "address mtrr\n");
190
191	for (i = 0; i < mapcount; i++) {
192		map = &tempmaps[i];
193
194		if (map->type < 0 || map->type > 4)
195			type = "??";
196		else
197			type = types[map->type];
198
199		if (!map->mtrr)
200			yesno = "no";
201		else
202			yesno = "yes";
203
204		DRM_SYSCTL_PRINT(
205		    "%4d 0x%08lx 0x%08lx %4.4s  0x%02x 0x%08lx %s\n", i,
206		    map->offset, map->size, type, map->flags,
207		    (unsigned long)map->handle, yesno);
208	}
209	SYSCTL_OUT(req, "", 1);
210
211done:
212	free(tempmaps, DRM_MEM_DRIVER);
213	return retcode;
214}
215
216static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS
217{
218	struct drm_device	 *dev = arg1;
219	drm_device_dma_t *dma = dev->dma;
220	drm_device_dma_t tempdma;
221	int *templists;
222	int i;
223	char buf[128];
224	int retcode;
225
226	/* We can't hold the locks around DRM_SYSCTL_PRINT, so make a temporary
227	 * copy of the whole structure and the relevant data from buflist.
228	 */
229	DRM_LOCK();
230	if (dma == NULL) {
231		DRM_UNLOCK();
232		return 0;
233	}
234	DRM_SPINLOCK(&dev->dma_lock);
235	tempdma = *dma;
236	templists = malloc(sizeof(int) * dma->buf_count, DRM_MEM_DRIVER,
237	    M_NOWAIT);
238	for (i = 0; i < dma->buf_count; i++)
239		templists[i] = dma->buflist[i]->list;
240	dma = &tempdma;
241	DRM_SPINUNLOCK(&dev->dma_lock);
242	DRM_UNLOCK();
243
244	DRM_SYSCTL_PRINT("\n o     size count  free	 segs pages    kB\n");
245	for (i = 0; i <= DRM_MAX_ORDER; i++) {
246		if (dma->bufs[i].buf_count)
247			DRM_SYSCTL_PRINT("%2d %8d %5d %5d %5d %5d %5d\n",
248				       i,
249				       dma->bufs[i].buf_size,
250				       dma->bufs[i].buf_count,
251				       atomic_read(&dma->bufs[i]
252						   .freelist.count),
253				       dma->bufs[i].seg_count,
254				       dma->bufs[i].seg_count
255				       *(1 << dma->bufs[i].page_order),
256				       (dma->bufs[i].seg_count
257					* (1 << dma->bufs[i].page_order))
258				       * PAGE_SIZE / 1024);
259	}
260	DRM_SYSCTL_PRINT("\n");
261	for (i = 0; i < dma->buf_count; i++) {
262		if (i && !(i%32)) DRM_SYSCTL_PRINT("\n");
263		DRM_SYSCTL_PRINT(" %d", templists[i]);
264	}
265	DRM_SYSCTL_PRINT("\n");
266
267	SYSCTL_OUT(req, "", 1);
268done:
269	free(templists, DRM_MEM_DRIVER);
270	return retcode;
271}
272
273static int drm_clients_info DRM_SYSCTL_HANDLER_ARGS
274{
275	struct drm_device *dev = arg1;
276	struct drm_file *priv, *tempprivs;
277	char buf[128];
278	int retcode;
279	int privcount, i;
280
281	DRM_LOCK();
282
283	privcount = 0;
284	TAILQ_FOREACH(priv, &dev->files, link)
285		privcount++;
286
287	tempprivs = malloc(sizeof(struct drm_file) * privcount, DRM_MEM_DRIVER,
288	    M_NOWAIT);
289	if (tempprivs == NULL) {
290		DRM_UNLOCK();
291		return ENOMEM;
292	}
293	i = 0;
294	TAILQ_FOREACH(priv, &dev->files, link)
295		tempprivs[i++] = *priv;
296
297	DRM_UNLOCK();
298
299	DRM_SYSCTL_PRINT("\na dev	pid    uid	magic	  ioctls\n");
300	for (i = 0; i < privcount; i++) {
301		priv = &tempprivs[i];
302		DRM_SYSCTL_PRINT("%c %3d %5d %5d %10u %10lu\n",
303			       priv->authenticated ? 'y' : 'n',
304			       priv->minor,
305			       priv->pid,
306			       priv->uid,
307			       priv->magic,
308			       priv->ioctl_count);
309	}
310
311	SYSCTL_OUT(req, "", 1);
312done:
313	free(tempprivs, DRM_MEM_DRIVER);
314	return retcode;
315}
316