Skip to content
  • Tejun Heo's avatar
    block: don't release bdi while request_queue has live references · b02176f3
    Tejun Heo authored
    bdi's are initialized in two steps, bdi_init() and bdi_register(), but
    destroyed in a single step by bdi_destroy() which, for a bdi embedded
    in a request_queue, is called during blk_cleanup_queue() which makes
    the queue invisible and starts the draining of remaining usages.
    
    A request_queue's user can access the congestion state of the embedded
    bdi as long as it holds a reference to the queue.  As such, it may
    access the congested state of a queue which finished
    blk_cleanup_queue() but hasn't reached blk_release_queue() yet.
    Because the congested state was embedded in backing_dev_info which in
    turn is embedded in request_queue, accessing the congested state after
    bdi_destroy() was called was fine.  The bdi was destroyed but the
    memory region for the congested state remained accessible till the
    queue got released.
    
    a13f35e8
    
     ("writeback: don't embed root bdi_writeback_congested in
    bdi_writeback") changed the situation.  Now, the root congested state
    which is expected to be pinned while request_queue remains accessible
    is separately reference counted and the base ref is put during
    bdi_destroy().  This means that the root congested state may go away
    prematurely while the queue is between bdi_dstroy() and
    blk_cleanup_queue(), which was detected by Andrey's KASAN tests.
    
    The root cause of this problem is that bdi doesn't distinguish the two
    steps of destruction, unregistration and release, and now the root
    congested state actually requires a separate release step.  To fix the
    issue, this patch separates out bdi_unregister() and bdi_exit() from
    bdi_destroy().  bdi_unregister() is called from blk_cleanup_queue()
    and bdi_exit() from blk_release_queue().  bdi_destroy() is now just a
    simple wrapper calling the two steps back-to-back.
    
    While at it, the prototype of bdi_destroy() is moved right below
    bdi_setup_and_register() so that the counterpart operations are
    located together.
    
    Signed-off-by: default avatarTejun Heo <tj@kernel.org>
    Fixes: a13f35e8
    
     ("writeback: don't embed root bdi_writeback_congested in bdi_writeback")
    Cc: stable@vger.kernel.org # v4.2+
    Reported-and-tested-by: default avatarAndrey Konovalov <andreyknvl@google.com>
    Link: http://lkml.kernel.org/g/CAAeHK+zUJ74Zn17=rOyxacHU18SgCfC6bsYW=6kCY5GXJBwGfQ@mail.gmail.com
    
    
    Reviewed-by: default avatarJan Kara <jack@suse.com>
    Reviewed-by: default avatarJeff Moyer <jmoyer@redhat.com>
    Signed-off-by: default avatarJens Axboe <axboe@fb.com>
    b02176f3