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 /** @file drm_sysctl.c
25 * Implementation of various sysctls for controlling DRM behavior and reporting
26 * debug information.
27 */
28
29 #include "drmP.h"
30 #include "drm.h"
31
32 #include <sys/sysctl.h>
33
34 static int drm_name_info DRM_SYSCTL_HANDLER_ARGS;
35 static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS;
36 static int drm_clients_info DRM_SYSCTL_HANDLER_ARGS;
37 static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS;
38
39 struct drm_sysctl_list {
40 const char *name;
41 int (*f) DRM_SYSCTL_HANDLER_ARGS;
42 } drm_sysctl_list[] = {
43 {"name", drm_name_info},
44 {"vm", drm_vm_info},
45 {"clients", drm_clients_info},
46 {"bufs", drm_bufs_info},
47 };
48 #define DRM_SYSCTL_ENTRIES (sizeof(drm_sysctl_list)/sizeof(drm_sysctl_list[0]))
49
50 struct drm_sysctl_info {
51 #if defined(__FreeBSD__)
52 struct sysctl_ctx_list ctx;
53 char name[2];
54 #elif defined(__NetBSD__)
55 const struct sysctlnode *dri, *dri_card, *dri_debug;
56 const struct sysctlnode *dri_rest[DRM_SYSCTL_ENTRIES];
57 char name[7];
58 struct sysctllog *log;
59 #endif
60 };
61
drm_sysctl_init(struct drm_device * dev)62 int drm_sysctl_init(struct drm_device *dev)
63 {
64 struct drm_sysctl_info *info;
65 #if defined(__FreeBSD__)
66 struct sysctl_oid *oid;
67 struct sysctl_oid *top, *drioid;
68 #endif
69 int i;
70
71 info = malloc(sizeof *info, DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
72 if ( !info )
73 return 1;
74 dev->sysctl = info;
75
76 #if defined(__FreeBSD__)
77 /* Add the sysctl node for DRI if it doesn't already exist */
78 drioid = SYSCTL_ADD_NODE( &info->ctx, &sysctl__hw_children, OID_AUTO, "dri", CTLFLAG_RW, NULL, "DRI Graphics");
79 if (!drioid)
80 return 1;
81
82 /* Find the next free slot under hw.dri */
83 i = 0;
84 SLIST_FOREACH(oid, SYSCTL_CHILDREN(drioid), oid_link) {
85 if (i <= oid->oid_arg2)
86 i = oid->oid_arg2 + 1;
87 }
88 if (i>9)
89 return 1;
90
91 /* Add the hw.dri.x for our device */
92 info->name[0] = '0' + i;
93 info->name[1] = 0;
94 top = SYSCTL_ADD_NODE( &info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, info->name, CTLFLAG_RW, NULL, NULL);
95 if (!top)
96 return 1;
97
98 for (i = 0; i < DRM_SYSCTL_ENTRIES; i++) {
99 oid = SYSCTL_ADD_OID(&info->ctx,
100 SYSCTL_CHILDREN(top),
101 OID_AUTO,
102 drm_sysctl_list[i].name,
103 CTLTYPE_INT | CTLFLAG_RD,
104 dev,
105 0,
106 drm_sysctl_list[i].f,
107 "A",
108 NULL);
109 if (!oid)
110 return 1;
111 }
112 SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(top), OID_AUTO, "debug",
113 CTLFLAG_RW, &drm_debug_flag, sizeof(drm_debug_flag),
114 "Enable debugging output");
115 #elif defined(__NetBSD__)
116 sysctl_createv(&info->log, 0, NULL, &info->dri,
117 CTLFLAG_READWRITE, CTLTYPE_NODE,
118 "dri", SYSCTL_DESCR("DRI Graphics"), NULL, 0, NULL, 0,
119 CTL_HW, CTL_CREATE);
120 snprintf(info->name, 7, "card%d", minor(dev->kdev));
121 sysctl_createv(&info->log, 0, NULL, &info->dri_card,
122 CTLFLAG_READWRITE, CTLTYPE_NODE,
123 info->name, NULL, NULL, 0, NULL, 0,
124 CTL_HW, info->dri->sysctl_num, CTL_CREATE);
125 for (i = 0; i < DRM_SYSCTL_ENTRIES; i++)
126 sysctl_createv(&info->log, 0, NULL, &(info->dri_rest[i]),
127 CTLFLAG_READONLY, CTLTYPE_STRING,
128 drm_sysctl_list[i].name, NULL,
129 drm_sysctl_list[i].f, 0, (void *)dev,
130 sizeof(struct drm_device*),
131 CTL_HW,
132 info->dri->sysctl_num,
133 info->dri_card->sysctl_num, CTL_CREATE);
134 sysctl_createv(&info->log, 0, NULL, &info->dri_debug,
135 CTLFLAG_READWRITE, CTLTYPE_INT,
136 "debug", SYSCTL_DESCR("Enable debugging output"),
137 NULL, 0,
138 &drm_debug_flag, sizeof(drm_debug_flag),
139 CTL_HW, info->dri->sysctl_num, CTL_CREATE);
140 #endif
141
142 return 0;
143 }
144
drm_sysctl_cleanup(struct drm_device * dev)145 int drm_sysctl_cleanup(struct drm_device *dev)
146 {
147 #if defined(__FreeBSD__)
148 int error;
149 error = sysctl_ctx_free( &dev->sysctl->ctx );
150
151 free(dev->sysctl, DRM_MEM_DRIVER);
152 dev->sysctl = NULL;
153
154 return error;
155 #elif defined(__NetBSD__)
156 sysctl_teardown(&dev->sysctl->log);
157
158 free(dev->sysctl, DRM_MEM_DRIVER);
159 dev->sysctl = NULL;
160
161 return 0;
162 #endif
163 }
164
165 #ifdef __NetBSD__
166 #define SYSCTL_OUT(x, y, z) \
167 drm_sysctl_out(oldp, oldlenp, &len, y, z, &error, &retcode);
168
169 static int
drm_sysctl_out(void * oldp,size_t * oldlenp,size_t * lenp,const char * buf,size_t buflen,int * errorp,int * retcodep)170 drm_sysctl_out(void *oldp, size_t *oldlenp, size_t *lenp,
171 const char *buf, size_t buflen,
172 int *errorp, int *retcodep)
173 {
174 size_t copylen;
175 int error = 0;
176
177 /*
178 * If there's room left in the user buffer,
179 * copy out as much data as there is room for.
180 */
181
182 if (*lenp < *oldlenp) {
183 copylen = MIN(buflen, *oldlenp - *lenp);
184 error = copyout(buf, (char *)oldp + *lenp, copylen);
185 if (error) {
186 *errorp = error;
187 }
188 } else {
189 copylen = 0;
190 }
191 *lenp += buflen;
192
193 /*
194 * If we didn't copy everything, remember that we should
195 * return ENOMEM at the end.
196 */
197
198 if (copylen < buflen && *errorp == 0) {
199 *errorp = ENOMEM;
200 }
201
202 /*
203 * If this is the final call (indicated by the buffer
204 * being the string terminator byte), return the
205 * total space required in *oldlenp and return
206 * the saved error in *retcodep.
207 */
208
209 if (buflen == 1 && *buf == 0) {
210 *oldlenp = *lenp;
211 *retcodep = *errorp;
212 }
213 return error;
214 }
215
216 #endif
217
218 #define DRM_SYSCTL_PRINT(fmt, arg...) \
219 do { \
220 snprintf(buf, sizeof(buf), fmt, ##arg); \
221 retcode = SYSCTL_OUT(req, buf, strlen(buf)); \
222 if (retcode) \
223 goto done; \
224 } while (0)
225
226 static int drm_name_info DRM_SYSCTL_HANDLER_ARGS
227 {
228 #if defined(__FreeBSD__)
229 struct drm_device *dev = arg1;
230 #elif defined(__NetBSD__)
231 struct drm_device *dev = rnode->sysctl_data;
232 size_t len = 0;
233 int error = 0;
234 #endif
235 char buf[128];
236 int retcode;
237 int hasunique = 0;
238
239 #if defined(__FreeBSD__)
240 DRM_SYSCTL_PRINT("%s 0x%x", dev->driver->name, dev2udev(dev->devnode));
241 #elif defined(__NetBSD__)
242 if (oldp == NULL)
243 return EINVAL;
244 *((char*)oldp) = '\0';
245
246 DRM_SYSCTL_PRINT("%s", dev->driver->name);
247 #endif
248
249 DRM_LOCK();
250 if (dev->unique) {
251 snprintf(buf, sizeof(buf), " %s", dev->unique);
252 hasunique = 1;
253 }
254 DRM_UNLOCK();
255
256 if (hasunique)
257 SYSCTL_OUT(req, buf, strlen(buf));
258
259 SYSCTL_OUT(req, "", 1);
260
261 done:
262 return retcode;
263 }
264
265 static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS
266 {
267 #if defined(__FreeBSD__)
268 struct drm_device *dev = arg1;
269 #elif defined(__NetBSD__)
270 struct drm_device *dev = rnode->sysctl_data;
271 size_t len = 0;
272 int error = 0;
273 #endif
274 drm_local_map_t *map, *tempmaps;
275 const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "GEM", "TTM" };
276 const char *type, *yesno;
277 int i, mapcount;
278 char buf[128];
279 int retcode;
280
281 /* We can't hold the lock while doing SYSCTL_OUTs, so allocate a
282 * temporary copy of all the map entries and then SYSCTL_OUT that.
283 */
284 DRM_LOCK();
285
286 mapcount = 0;
287 TAILQ_FOREACH(map, &dev->maplist, link)
288 mapcount++;
289
290 tempmaps = malloc(sizeof(drm_local_map_t) * mapcount, DRM_MEM_DRIVER,
291 M_WAITOK);
292
293 i = 0;
294 TAILQ_FOREACH(map, &dev->maplist, link)
295 tempmaps[i++] = *map;
296
297 DRM_UNLOCK();
298
299 DRM_SYSCTL_PRINT("\nslot offset size "
300 "type flags address mtrr\n");
301
302 for (i = 0; i < mapcount; i++) {
303 map = &tempmaps[i];
304
305 if (map->type > 4)
306 type = "??";
307 else
308 type = types[map->type];
309
310 if (!map->mtrr)
311 yesno = "no";
312 else
313 yesno = "yes";
314
315 DRM_SYSCTL_PRINT(
316 "%4d 0x%016lx 0x%08lx %4.4s 0x%02x 0x%016lx %s\n", i,
317 map->offset, map->size, type, map->flags,
318 (unsigned long)map->handle, yesno);
319 }
320 SYSCTL_OUT(req, "", 1);
321
322 done:
323 free(tempmaps, DRM_MEM_DRIVER);
324 return retcode;
325 }
326
327 static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS
328 {
329 #if defined(__FreeBSD__)
330 struct drm_device *dev = arg1;
331 #elif defined(__NetBSD__)
332 struct drm_device *dev = rnode->sysctl_data;
333 size_t len = 0;
334 int error = 0;
335 #endif
336 drm_device_dma_t *dma = dev->dma;
337 drm_device_dma_t tempdma;
338 int *templists;
339 int i;
340 char buf[128];
341 int retcode;
342
343 /* We can't hold the locks around DRM_SYSCTL_PRINT, so make a temporary
344 * copy of the whole structure and the relevant data from buflist.
345 */
346 DRM_LOCK();
347 if (dma == NULL) {
348 DRM_UNLOCK();
349 return 0;
350 }
351 DRM_SPINLOCK(&dev->dma_lock);
352 tempdma = *dma;
353 templists = malloc(sizeof(int) * dma->buf_count, DRM_MEM_DRIVER,
354 M_WAITOK);
355 for (i = 0; i < dma->buf_count; i++)
356 templists[i] = dma->buflist[i]->list;
357 dma = &tempdma;
358 DRM_SPINUNLOCK(&dev->dma_lock);
359 DRM_UNLOCK();
360
361 DRM_SYSCTL_PRINT("\n o size count free segs pages kB\n");
362 for (i = 0; i <= DRM_MAX_ORDER; i++) {
363 if (dma->bufs[i].buf_count)
364 DRM_SYSCTL_PRINT("%2d %8d %5d %5d %5d %5d %5d\n",
365 i,
366 dma->bufs[i].buf_size,
367 dma->bufs[i].buf_count,
368 atomic_read(&dma->bufs[i]
369 .freelist.count),
370 dma->bufs[i].seg_count,
371 dma->bufs[i].seg_count
372 *(1 << dma->bufs[i].page_order),
373 (dma->bufs[i].seg_count
374 * (1 << dma->bufs[i].page_order))
375 * PAGE_SIZE / 1024);
376 }
377 DRM_SYSCTL_PRINT("\n");
378 for (i = 0; i < dma->buf_count; i++) {
379 if (i && !(i%32)) DRM_SYSCTL_PRINT("\n");
380 DRM_SYSCTL_PRINT(" %d", templists[i]);
381 }
382 DRM_SYSCTL_PRINT("\n");
383
384 SYSCTL_OUT(req, "", 1);
385 done:
386 free(templists, DRM_MEM_DRIVER);
387 return retcode;
388 }
389
390 static int drm_clients_info DRM_SYSCTL_HANDLER_ARGS
391 {
392 #if defined(__FreeBSD__)
393 struct drm_device *dev = arg1;
394 #elif defined(__NetBSD__)
395 struct drm_device *dev = rnode->sysctl_data;
396 size_t len = 0;
397 int error = 0;
398 #endif
399 struct drm_file *priv, *tempprivs;
400 char buf[128];
401 int retcode;
402 int privcount, i;
403
404 DRM_LOCK();
405
406 privcount = 0;
407 TAILQ_FOREACH(priv, &dev->files, link)
408 privcount++;
409
410 tempprivs = malloc(sizeof(struct drm_file) * privcount, DRM_MEM_DRIVER,
411 M_WAITOK);
412 i = 0;
413 TAILQ_FOREACH(priv, &dev->files, link)
414 tempprivs[i++] = *priv;
415
416 DRM_UNLOCK();
417
418 DRM_SYSCTL_PRINT("\na dev pid uid magic ioctls\n");
419 for (i = 0; i < privcount; i++) {
420 priv = &tempprivs[i];
421 DRM_SYSCTL_PRINT("%c %3d %5d %5d %10u %10lu\n",
422 priv->authenticated ? 'y' : 'n',
423 priv->minor,
424 priv->pid,
425 priv->uid,
426 priv->magic,
427 priv->ioctl_count);
428 }
429
430 SYSCTL_OUT(req, "", 1);
431 done:
432 free(tempprivs, DRM_MEM_DRIVER);
433 return retcode;
434 }
435