I have been looking over dtrace output and trying to read drm code.
While what I describe is not enough to explain the 16 MB, it is an
apparenet leak of a high-level structure that I would expect has a lot
of suballocations.
alloc 32776
netbsd`kmem_intr_zalloc+0x11
netbsd`linux_sg_alloc_table+0x35
netbsd`shmem_get_pages+0x98
netbsd`____i915_gem_object_get_pages+0x25
netbsd`__i915_gem_object_get_pages+0x45
netbsd`i915_vma_pin+0x16e
netbsd`eb_lookup_vmas+0x1b3
netbsd`i915_gem_do_execbuffer+0x61f
netbsd`i915_gem_execbuffer2_ioctl+0x1be
netbsd`drm_ioctl+0x246
netbsd`drm_ioctl_shim+0x35
netbsd`sys_ioctl+0x4ca
netbsd`syscall+0x9a
netbsd`handle_syscall+0x2d
5
free 32776
netbsd`linux_sg_free_table+0x37
netbsd`shmem_put_pages+0x66
netbsd`__i915_gem_object_put_pages+0x8c
netbsd`__i915_gem_free_objects+0x13f
netbsd`linux_workqueue_thread+0x101
netbsd`lwp_trampoline+0x17
4
It seems that i915_gem_execbuffer2_ioctl is supposed to record this
somehow so that it's freed on close. And that 5 are created, and one
freed. There is an error path, and maybe a scheme to pass pages to
userland. I am wondering if one of the ioctls doesn't go well, and that
leaks those pages - but I'm way out on a limb. The only thing that
seems clear is that when I run glxgears once, there are 5 allocs and 4
frees.
In
src/sys/external/bsd/drm2/dist/drm/i915/gem/i915_gem_execbuffer.c:
int
i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_i915_gem_execbuffer2 *args = data;
struct drm_i915_gem_exec_object2 *exec2_list;
struct drm_syncobj **fences = NULL;
const size_t count = args->buffer_count;
int err;
if (!check_buffer_count(count)) {
DRM_DEBUG("execbuf2 with %zd buffers\n", count);
return -EINVAL;
}
err = i915_gem_check_execbuffer(args);
if (err)
return err;
err = i915_gem_do_execbuffer(dev, file, args, exec2_list, fences);
/*
* Now that we have begun execution of the batchbuffer, we ignore
* any new error after this point. Also given that we have already
* updated the associated relocations, we try to write out the current
* object locations irrespective of any error.
*/
if (args->flags & __EXEC_HAS_RELOC) {
struct drm_i915_gem_exec_object2 __user *user_exec_list =
u64_to_user_ptr(args->buffers_ptr);
unsigned int i;
/* Copy the new buffer offsets back to the user's exec list. */
/*
* Note: count * sizeof(*user_exec_list) does not overflow,
* because we checked 'count' in check_buffer_count().
*
* And this range already got effectively checked earlier
* when we did the "copy_from_user()" above.
*/
if (!user_access_begin(user_exec_list, count * sizeof(*user_exec_list)))
goto end;
for (i = 0; i < args->buffer_count; i++) {
if (!(exec2_list[i].offset & UPDATE))
continue;
exec2_list[i].offset =
gen8_canonical_addr(exec2_list[i].offset & PIN_OFFSET_MASK);
unsafe_put_user(exec2_list[i].offset,
&user_exec_list[i].offset,
end_user);
}
end_user:
user_access_end();
end:;
}
args->flags &= ~__I915_EXEC_UNKNOWN_FLAGS;
put_fence_array(args, fences);
kvfree(exec2_list);
return err;
}