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 * $FreeBSD: src/sys/dev/drm2/drm_sysctl.c,v 1.1 2012/05/22 11:07:44 kib Exp $ 24 */ 25 26 /** @file drm_sysctl.c 27 * Implementation of various sysctls for controlling DRM behavior and reporting 28 * debug information. 29 */ 30 31 #include <sys/conf.h> 32 #include <sys/sysctl.h> 33 #include <sys/types.h> 34 35 #include <drm/drmP.h> 36 37 static int drm_name_info DRM_SYSCTL_HANDLER_ARGS; 38 static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS; 39 static int drm_clients_info DRM_SYSCTL_HANDLER_ARGS; 40 static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS; 41 static int drm_vblank_info DRM_SYSCTL_HANDLER_ARGS; 42 43 struct drm_sysctl_list { 44 const char *name; 45 int (*f) DRM_SYSCTL_HANDLER_ARGS; 46 } drm_sysctl_list[] = { 47 {"name", drm_name_info}, 48 {"vm", drm_vm_info}, 49 {"clients", drm_clients_info}, 50 {"bufs", drm_bufs_info}, 51 {"vblank", drm_vblank_info}, 52 }; 53 #define DRM_SYSCTL_ENTRIES NELEM(drm_sysctl_list) 54 55 struct drm_sysctl_info { 56 struct sysctl_ctx_list ctx; 57 char name[2]; 58 }; 59 60 int drm_sysctl_init(struct drm_device *dev) 61 { 62 struct drm_sysctl_info *info; 63 struct sysctl_oid *oid; 64 struct sysctl_oid *top, *drioid; 65 int i; 66 67 info = kmalloc(sizeof *info, M_DRM, M_WAITOK | M_ZERO); 68 if ( !info ) 69 return 1; 70 dev->sysctl = info; 71 72 /* Add the sysctl node for DRI if it doesn't already exist */ 73 drioid = SYSCTL_ADD_NODE(&info->ctx, &sysctl__hw_children, OID_AUTO, 74 "dri", CTLFLAG_RW, NULL, "DRI Graphics"); 75 if (!drioid) 76 return 1; 77 78 /* Find the next free slot under hw.dri */ 79 i = 0; 80 SLIST_FOREACH(oid, SYSCTL_CHILDREN(drioid), oid_link) { 81 if (i <= oid->oid_arg2) 82 i = oid->oid_arg2 + 1; 83 } 84 if (i>9) 85 return 1; 86 87 dev->sysctl_node_idx = i; 88 /* Add the hw.dri.x for our device */ 89 info->name[0] = '0' + i; 90 info->name[1] = 0; 91 top = SYSCTL_ADD_NODE(&info->ctx, SYSCTL_CHILDREN(drioid), 92 OID_AUTO, info->name, CTLFLAG_RW, NULL, NULL); 93 if (!top) 94 return 1; 95 96 for (i = 0; i < DRM_SYSCTL_ENTRIES; i++) { 97 oid = SYSCTL_ADD_OID(&info->ctx, 98 SYSCTL_CHILDREN(top), 99 OID_AUTO, 100 drm_sysctl_list[i].name, 101 CTLTYPE_STRING | CTLFLAG_RD, 102 dev, 103 0, 104 drm_sysctl_list[i].f, 105 "A", 106 NULL); 107 if (!oid) 108 return 1; 109 } 110 SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, "debug", 111 CTLFLAG_RW, &drm_debug, sizeof(drm_debug), 112 "Enable debugging output"); 113 SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, "notyet", 114 CTLFLAG_RW, &drm_notyet_flag, sizeof(drm_debug), 115 "Enable notyet reminders"); 116 117 if (dev->driver->sysctl_init != NULL) 118 dev->driver->sysctl_init(dev, &info->ctx, top); 119 120 SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, 121 "vblank_offdelay", CTLFLAG_RW, &drm_vblank_offdelay, 122 sizeof(drm_vblank_offdelay), 123 ""); 124 SYSCTL_ADD_INT(&info->ctx, SYSCTL_CHILDREN(drioid), OID_AUTO, 125 "timestamp_precision", CTLFLAG_RW, &drm_timestamp_precision, 126 sizeof(drm_timestamp_precision), 127 ""); 128 129 return (0); 130 } 131 132 int drm_sysctl_cleanup(struct drm_device *dev) 133 { 134 int error; 135 136 error = sysctl_ctx_free(&dev->sysctl->ctx); 137 drm_free(dev->sysctl, M_DRM); 138 dev->sysctl = NULL; 139 if (dev->driver->sysctl_cleanup != NULL) 140 dev->driver->sysctl_cleanup(dev); 141 142 return (error); 143 } 144 145 #define DRM_SYSCTL_PRINT(fmt, arg...) \ 146 do { \ 147 ksnprintf(buf, sizeof(buf), fmt, ##arg); \ 148 retcode = SYSCTL_OUT(req, buf, strlen(buf)); \ 149 if (retcode) \ 150 goto done; \ 151 } while (0) 152 153 static int drm_name_info DRM_SYSCTL_HANDLER_ARGS 154 { 155 struct drm_device *dev = arg1; 156 char buf[128]; 157 int retcode; 158 int hasunique = 0; 159 160 DRM_SYSCTL_PRINT("%s 0x%x", dev->driver->name, dev2udev(dev->devnode)); 161 162 DRM_LOCK(dev); 163 if (dev->unique) { 164 ksnprintf(buf, sizeof(buf), " %s", dev->unique); 165 hasunique = 1; 166 } 167 DRM_UNLOCK(dev); 168 169 if (hasunique) 170 SYSCTL_OUT(req, buf, strlen(buf)); 171 172 SYSCTL_OUT(req, "", 1); 173 174 done: 175 return retcode; 176 } 177 178 /** 179 * Called when "/proc/dri/.../vm" is read. 180 * 181 * Prints information about all mappings in drm_device::maplist. 182 */ 183 static int drm_vm_info DRM_SYSCTL_HANDLER_ARGS 184 { 185 char buf[128]; 186 int retcode; 187 struct drm_device *dev = arg1; 188 struct drm_local_map *map; 189 struct drm_map_list *r_list; 190 191 /* Hardcoded from _DRM_FRAME_BUFFER, 192 _DRM_REGISTERS, _DRM_SHM, _DRM_AGP, and 193 _DRM_SCATTER_GATHER and _DRM_CONSISTENT */ 194 const char *types[] = { "FB", "REG", "SHM", "AGP", "SG", "PCI" }; 195 const char *type; 196 int i; 197 198 DRM_LOCK(dev); 199 DRM_SYSCTL_PRINT("\nslot offset size " 200 "type flags address handle mtrr\n"); 201 i = 0; 202 list_for_each_entry(r_list, &dev->maplist, head) { 203 map = r_list->map; 204 if (!map) 205 continue; 206 if (map->type < 0 || map->type > 5) 207 type = "??"; 208 else 209 type = types[map->type]; 210 211 DRM_SYSCTL_PRINT("%4d 0x%016llx 0x%08lx %4.4s 0x%02x 0x%08lx ", 212 i, 213 (unsigned long long)map->offset, 214 map->size, type, map->flags, 215 (unsigned long) r_list->user_token); 216 if (map->mtrr < 0) 217 DRM_SYSCTL_PRINT("none\n"); 218 else 219 DRM_SYSCTL_PRINT("%4d\n", map->mtrr); 220 i++; 221 222 } 223 SYSCTL_OUT(req, "", 1); 224 DRM_UNLOCK(dev); 225 226 done: 227 return 0; 228 } 229 230 static int drm_bufs_info DRM_SYSCTL_HANDLER_ARGS 231 { 232 struct drm_device *dev = arg1; 233 drm_device_dma_t *dma = dev->dma; 234 drm_device_dma_t tempdma; 235 int *templists; 236 int i; 237 char buf[128]; 238 int retcode; 239 240 /* We can't hold the locks around DRM_SYSCTL_PRINT, so make a temporary 241 * copy of the whole structure and the relevant data from buflist. 242 */ 243 DRM_LOCK(dev); 244 if (dma == NULL) { 245 DRM_UNLOCK(dev); 246 return 0; 247 } 248 spin_lock(&dev->dma_lock); 249 tempdma = *dma; 250 templists = kmalloc(sizeof(int) * dma->buf_count, M_DRM, 251 M_NOWAIT); 252 for (i = 0; i < dma->buf_count; i++) 253 templists[i] = dma->buflist[i]->list; 254 dma = &tempdma; 255 spin_unlock(&dev->dma_lock); 256 DRM_UNLOCK(dev); 257 258 DRM_SYSCTL_PRINT("\n o size count free segs pages kB\n"); 259 for (i = 0; i <= DRM_MAX_ORDER; i++) { 260 if (dma->bufs[i].buf_count) 261 DRM_SYSCTL_PRINT("%2d %8d %5d %5d %5d %5d %5d\n", 262 i, 263 dma->bufs[i].buf_size, 264 dma->bufs[i].buf_count, 265 atomic_read(&dma->bufs[i] 266 .freelist.count), 267 dma->bufs[i].seg_count, 268 dma->bufs[i].seg_count 269 *(1 << dma->bufs[i].page_order), 270 (dma->bufs[i].seg_count 271 * (1 << dma->bufs[i].page_order)) 272 * (int)PAGE_SIZE / 1024); 273 } 274 DRM_SYSCTL_PRINT("\n"); 275 for (i = 0; i < dma->buf_count; i++) { 276 if (i && !(i%32)) DRM_SYSCTL_PRINT("\n"); 277 DRM_SYSCTL_PRINT(" %d", templists[i]); 278 } 279 DRM_SYSCTL_PRINT("\n"); 280 281 SYSCTL_OUT(req, "", 1); 282 done: 283 drm_free(templists, M_DRM); 284 return retcode; 285 } 286 287 static int drm_clients_info DRM_SYSCTL_HANDLER_ARGS 288 { 289 struct drm_device *dev = arg1; 290 struct drm_file *priv, *tempprivs; 291 char buf[128]; 292 int retcode; 293 int privcount, i; 294 295 DRM_LOCK(dev); 296 297 privcount = 0; 298 list_for_each_entry(priv, &dev->filelist, lhead) 299 privcount++; 300 301 tempprivs = kmalloc(sizeof(struct drm_file) * privcount, M_DRM, 302 M_NOWAIT); 303 if (tempprivs == NULL) { 304 DRM_UNLOCK(dev); 305 return ENOMEM; 306 } 307 i = 0; 308 list_for_each_entry(priv, &dev->filelist, lhead) 309 tempprivs[i++] = *priv; 310 311 DRM_UNLOCK(dev); 312 313 DRM_SYSCTL_PRINT( 314 "\na dev pid uid magic ioctls\n"); 315 for (i = 0; i < privcount; i++) { 316 priv = &tempprivs[i]; 317 DRM_SYSCTL_PRINT("%c %-12s %5d %5d %10u %10lu\n", 318 priv->authenticated ? 'y' : 'n', 319 devtoname(priv->dev->devnode), 320 priv->pid, 321 priv->uid, 322 priv->magic, 323 priv->ioctl_count); 324 } 325 326 SYSCTL_OUT(req, "", 1); 327 done: 328 drm_free(tempprivs, M_DRM); 329 return retcode; 330 } 331 332 static int drm_vblank_info DRM_SYSCTL_HANDLER_ARGS 333 { 334 struct drm_device *dev = arg1; 335 char buf[128]; 336 int retcode; 337 int i; 338 339 DRM_SYSCTL_PRINT("\ncrtc ref count last enabled inmodeset\n"); 340 DRM_LOCK(dev); 341 if (dev->_vblank_count == NULL) 342 goto done; 343 for (i = 0 ; i < dev->num_crtcs ; i++) { 344 DRM_SYSCTL_PRINT(" %02d %02d %08d %08d %02d %02d\n", 345 i, dev->vblank_refcount[i].counter, 346 dev->_vblank_count[i].counter, 347 dev->last_vblank[i], 348 dev->vblank_enabled[i], 349 dev->vblank_inmodeset[i]); 350 } 351 done: 352 DRM_UNLOCK(dev); 353 354 SYSCTL_OUT(req, "", -1); 355 return retcode; 356 } 357