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