• Mikulas Patocka's avatar
    dm: fix narrow race for REQ_NOWAIT bios being issued despite no support · 1ee88de3
    Mikulas Patocka authored
    Starting with the commit 63a225c9fd20, device mapper has an optimization
    that it will take cheaper table lock (dm_get_live_table_fast instead of
    dm_get_live_table) if the bio has REQ_NOWAIT. The bios with REQ_NOWAIT
    must not block in the target request routine, if they did, we would be
    blocking while holding rcu_read_lock, which is prohibited.
    
    The targets that are suitable for REQ_NOWAIT optimization (and that don't
    block in the map routine) have the flag DM_TARGET_NOWAIT set. Device
    mapper will test if all the targets and all the devices in a table
    support nowait (see the function dm_table_supports_nowait) and it will set
    or clear the QUEUE_FLAG_NOWAIT flag on its request queue according to
    this check.
    
    There's a test in submit_bio_noacct: "if ((bio->bi_opf & REQ_NOWAIT) &&
    !blk_queue_nowait(q)) goto not_supported" - this will make sure that
    REQ_NOWAIT bios can't enter a request queue that doesn't support them.
    
    This mechanism works to prevent REQ_NOWAIT bios from reaching dm targets
    that don't support the REQ_NOWAIT flag (and that may block in the map
    routine) - except that there is a small race condition:
    
    submit_bio_noacct checks if the queue has the QUEUE_FLAG_NOWAIT without
    holding any locks. Immediatelly after this check, the device mapper table
    may be reloaded with a table that doesn't support REQ_NOWAIT (for example,
    if we start moving the logical volume or if we activate a snapshot).
    However the REQ_NOWAIT bio that already passed the check in
    submit_bio_noacct would be sent to device mapper, where it could be
    redirected to a dm target that doesn't support REQ_NOWAIT - the result is
    sleeping while we hold rcu_read_lock.
    
    In order to fix this race, we double-check if the target supports
    REQ_NOWAIT while we hold the table lock (so that the table can't change
    under us).
    
    Fixes: 563a225c ("dm: introduce dm_{get,put}_live_table_bio called from dm_submit_bio")
    Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
    Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
    1ee88de3
dm.c 75.5 KB