NetBSD-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: wired memory leak?



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;
}


Home | Main Index | Thread Index | Old Index