• Christian Brauner's avatar
    file, i915: fix file reference for mmap_singleton() · 61d4fb0b
    Christian Brauner authored
    Today we got a report at [1] for rcu stalls on the i915 testsuite in [2]
    due to the conversion of files to SLAB_TYPSSAFE_BY_RCU. Afaict,
    get_file_rcu() goes into an infinite loop trying to carefully verify
    that i915->gem.mmap_singleton hasn't changed - see the splat below.
    
    So I stared at this code to figure out what it actually does. It seems
    that the i915->gem.mmap_singleton pointer itself never had rcu semantics.
    
    The i915->gem.mmap_singleton is replaced in
    file->f_op->release::singleton_release():
    
            static int singleton_release(struct inode *inode, struct file *file)
            {
                    struct drm_i915_private *i915 = file->private_data;
    
                    cmpxchg(&i915->gem.mmap_singleton, file, NULL);
                    drm_dev_put(&i915->drm);
    
                    return 0;
            }
    
    The cmpxchg() is ordered against a concurrent update of
    i915->gem.mmap_singleton from mmap_singleton(). IOW, when
    mmap_singleton() fails to get a reference on i915->gem.mmap_singleton:
    
    While mmap_singleton() does
    
            rcu_read_lock();
            file = get_file_rcu(&i915->gem.mmap_singleton);
            rcu_read_unlock();
    
    it allocates a new file via anon_inode_getfile() and does
    
            smp_store_mb(i915->gem.mmap_singleton, file);
    
    So, then what happens in the case of this bug is that at some point
    fput() is called and drops the file->f_count to zero leaving the pointer
    in i915->gem.mmap_singleton in tact.
    
    Now, there might be delays until
    file->f_op->release::singleton_release() is called and
    i915->gem.mmap_singleton is set to NULL.
    
    Say concurrently another task hits mmap_singleton() and does:
    
            rcu_read_lock();
            file = get_file_rcu(&i915->gem.mmap_singleton);
            rcu_read_unlock();
    
    When get_file_rcu() fails to get a reference via atomic_inc_not_zero()
    it will try the reload from i915->gem.mmap_singleton expecting it to be
    NULL, assuming it has comparable semantics as we expect in
    __fget_files_rcu().
    
    But it hasn't so it reloads the same pointer again, trying the same
    atomic_inc_not_zero() again and doing so until
    file->f_op->release::singleton_release() of the old file has been
    called.
    
    So, in contrast to __fget_files_rcu() here we want to not retry when
    atomic_inc_not_zero() has failed. We only want to retry in case we
    managed to get a reference but the pointer did change on reload.
    
    <3> [511.395679] rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
    <3> [511.395716] rcu:   Tasks blocked on level-1 rcu_node (CPUs 0-9): P6238
    <3> [511.395934] rcu:   (detected by 16, t=65002 jiffies, g=123977, q=439 ncpus=20)
    <6> [511.395944] task:i915_selftest   state:R  running task     stack:10568 pid:6238  tgid:6238  ppid:1001   flags:0x00004002
    <6> [511.395962] Call Trace:
    <6> [511.395966]  <TASK>
    <6> [511.395974]  ? __schedule+0x3a8/0xd70
    <6> [511.395995]  ? asm_sysvec_apic_timer_interrupt+0x1a/0x20
    <6> [511.396003]  ? lockdep_hardirqs_on+0xc3/0x140
    <6> [511.396013]  ? asm_sysvec_apic_timer_interrupt+0x1a/0x20
    <6> [511.396029]  ? get_file_rcu+0x10/0x30
    <6> [511.396039]  ? get_file_rcu+0x10/0x30
    <6> [511.396046]  ? i915_gem_object_mmap+0xbc/0x450 [i915]
    <6> [511.396509]  ? i915_gem_mmap+0x272/0x480 [i915]
    <6> [511.396903]  ? mmap_region+0x253/0xb60
    <6> [511.396925]  ? do_mmap+0x334/0x5c0
    <6> [511.396939]  ? vm_mmap_pgoff+0x9f/0x1c0
    <6> [511.396949]  ? rcu_is_watching+0x11/0x50
    <6> [511.396962]  ? igt_mmap_offset+0xfc/0x110 [i915]
    <6> [511.397376]  ? __igt_mmap+0xb3/0x570 [i915]
    <6> [511.397762]  ? igt_mmap+0x11e/0x150 [i915]
    <6> [511.398139]  ? __trace_bprintk+0x76/0x90
    <6> [511.398156]  ? __i915_subtests+0xbf/0x240 [i915]
    <6> [511.398586]  ? __pfx___i915_live_setup+0x10/0x10 [i915]
    <6> [511.399001]  ? __pfx___i915_live_teardown+0x10/0x10 [i915]
    <6> [511.399433]  ? __run_selftests+0xbc/0x1a0 [i915]
    <6> [511.399875]  ? i915_live_selftests+0x4b/0x90 [i915]
    <6> [511.400308]  ? i915_pci_probe+0x106/0x200 [i915]
    <6> [511.400692]  ? pci_device_probe+0x95/0x120
    <6> [511.400704]  ? really_probe+0x164/0x3c0
    <6> [511.400715]  ? __pfx___driver_attach+0x10/0x10
    <6> [511.400722]  ? __driver_probe_device+0x73/0x160
    <6> [511.400731]  ? driver_probe_device+0x19/0xa0
    <6> [511.400741]  ? __driver_attach+0xb6/0x180
    <6> [511.400749]  ? __pfx___driver_attach+0x10/0x10
    <6> [511.400756]  ? bus_for_each_dev+0x77/0xd0
    <6> [511.400770]  ? bus_add_driver+0x114/0x210
    <6> [511.400781]  ? driver_register+0x5b/0x110
    <6> [511.400791]  ? i915_init+0x23/0xc0 [i915]
    <6> [511.401153]  ? __pfx_i915_init+0x10/0x10 [i915]
    <6> [511.401503]  ? do_one_initcall+0x57/0x270
    <6> [511.401515]  ? rcu_is_watching+0x11/0x50
    <6> [511.401521]  ? kmalloc_trace+0xa3/0xb0
    <6> [511.401532]  ? do_init_module+0x5f/0x210
    <6> [511.401544]  ? load_module+0x1d00/0x1f60
    <6> [511.401581]  ? init_module_from_file+0x86/0xd0
    <6> [511.401590]  ? init_module_from_file+0x86/0xd0
    <6> [511.401613]  ? idempotent_init_module+0x17c/0x230
    <6> [511.401639]  ? __x64_sys_finit_module+0x56/0xb0
    <6> [511.401650]  ? do_syscall_64+0x3c/0x90
    <6> [511.401659]  ? entry_SYSCALL_64_after_hwframe+0x6e/0xd8
    <6> [511.401684]  </TASK>
    
    Link: [1]: https://lore.kernel.org/intel-gfx/SJ1PR11MB6129CB39EED831784C331BAFB9DEA@SJ1PR11MB6129.namprd11.prod.outlook.com
    Link: [2]: https://intel-gfx-ci.01.org/tree/linux-next/next-20231013/bat-dg2-11/igt@i915_selftest@live@mman.html#dmesg-warnings10963
    Cc: Jann Horn <jannh@google.com>,
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Link: https://lore.kernel.org/r/20231025-formfrage-watscheln-84526cd3bd7d@braunerSigned-off-by: default avatarChristian Brauner <brauner@kernel.org>
    61d4fb0b
file.c 35.5 KB