Home | History | Annotate | Line # | Download | only in bsd-core
      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 
     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 
    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
    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