• Kiyoshi Ueda's avatar
    dm: prepare for request based option · cec47e3d
    Kiyoshi Ueda authored
    This patch adds core functions for request-based dm.
    
    When struct mapped device (md) is initialized, md->queue has
    an I/O scheduler and the following functions are used for
    request-based dm as the queue functions:
        make_request_fn: dm_make_request()
        pref_fn:         dm_prep_fn()
        request_fn:      dm_request_fn()
        softirq_done_fn: dm_softirq_done()
        lld_busy_fn:     dm_lld_busy()
    Actual initializations are done in another patch (PATCH 2).
    
    Below is a brief summary of how request-based dm behaves, including:
      - making request from bio
      - cloning, mapping and dispatching request
      - completing request and bio
      - suspending md
      - resuming md
    
      bio to request
      ==============
      md->queue->make_request_fn() (dm_make_request()) calls __make_request()
      for a bio submitted to the md.
      Then, the bio is kept in the queue as a new request or merged into
      another request in the queue if possible.
    
      Cloning and Mapping
      ===================
      Cloning and mapping are done in md->queue->request_fn() (dm_request_fn()),
      when requests are dispatched after they are sorted by the I/O scheduler.
    
      dm_request_fn() checks busy state of underlying devices using
      target's busy() function and stops dispatching requests to keep them
      on the dm device's queue if busy.
      It helps better I/O merging, since no merge is done for a request
      once it is dispatched to underlying devices.
    
      Actual cloning and mapping are done in dm_prep_fn() and map_request()
      called from dm_request_fn().
      dm_prep_fn() clones not only request but also bios of the request
      so that dm can hold bio completion in error cases and prevent
      the bio submitter from noticing the error.
      (See the "Completion" section below for details.)
    
      After the cloning, the clone is mapped by target's map_rq() function
        and inserted to underlying device's queue using
        blk_insert_cloned_request().
    
      Completion
      ==========
      Request completion can be hooked by rq->end_io(), but then, all bios
      in the request will have been completed even error cases, and the bio
      submitter will have noticed the error.
      To prevent the bio completion in error cases, request-based dm clones
      both bio and request and hooks both bio->bi_end_io() and rq->end_io():
          bio->bi_end_io(): end_clone_bio()
          rq->end_io():     end_clone_request()
    
      Summary of the request completion flow is below:
      blk_end_request() for a clone request
        => blk_update_request()
           => bio->bi_end_io() == end_clone_bio() for each clone bio
              => Free the clone bio
              => Success: Complete the original bio (blk_update_request())
                 Error:   Don't complete the original bio
        => blk_finish_request()
           => rq->end_io() == end_clone_request()
              => blk_complete_request()
                 => dm_softirq_done()
                    => Free the clone request
                    => Success: Complete the original request (blk_end_request())
                       Error:   Requeue the original request
    
      end_clone_bio() completes the original request on the size of
      the original bio in successful cases.
      Even if all bios in the original request are completed by that
      completion, the original request must not be completed yet to keep
      the ordering of request completion for the stacking.
      So end_clone_bio() uses blk_update_request() instead of
      blk_end_request().
      In error cases, end_clone_bio() doesn't complete the original bio.
      It just frees the cloned bio and gives over the error handling to
      end_clone_request().
    
      end_clone_request(), which is called with queue lock held, completes
      the clone request and the original request in a softirq context
      (dm_softirq_done()), which has no queue lock, to avoid a deadlock
      issue on submission of another request during the completion:
          - The submitted request may be mapped to the same device
          - Request submission requires queue lock, but the queue lock
            has been held by itself and it doesn't know that
    
      The clone request has no clone bio when dm_softirq_done() is called.
      So target drivers can't resubmit it again even error cases.
      Instead, they can ask dm core for requeueing and remapping
      the original request in that cases.
    
      suspend
      =======
      Request-based dm uses stopping md->queue as suspend of the md.
      For noflush suspend, just stops md->queue.
    
      For flush suspend, inserts a marker request to the tail of md->queue.
      And dispatches all requests in md->queue until the marker comes to
      the front of md->queue.  Then, stops dispatching request and waits
      for the all dispatched requests to complete.
      After that, completes the marker request, stops md->queue and
      wake up the waiter on the suspend queue, md->wait.
    
      resume
      ======
      Starts md->queue.
    Signed-off-by: default avatarKiyoshi Ueda <k-ueda@ct.jp.nec.com>
    Signed-off-by: default avatarJun'ichi Nomura <j-nomura@ce.jp.nec.com>
    Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
    cec47e3d
dm.c 56.1 KB