dm thin: fix stacked bi_next usage
[~shefty/rdma-dev.git] / drivers / md / dm-thin.c
index c3087575fef0ffe5330c261a721abddef3f6771e..da2f0217df666fcc087ef9b28da40319a393435e 100644 (file)
@@ -124,7 +124,7 @@ struct cell {
        struct hlist_node list;
        struct bio_prison *prison;
        struct cell_key key;
-       unsigned count;
+       struct bio *holder;
        struct bio_list bios;
 };
 
@@ -220,55 +220,60 @@ static struct cell *__search_bucket(struct hlist_head *bucket,
  * This may block if a new cell needs allocating.  You must ensure that
  * cells will be unlocked even if the calling thread is blocked.
  *
- * Returns the number of entries in the cell prior to the new addition
- * or < 0 on failure.
+ * Returns 1 if the cell was already held, 0 if @inmate is the new holder.
  */
 static int bio_detain(struct bio_prison *prison, struct cell_key *key,
                      struct bio *inmate, struct cell **ref)
 {
-       int r;
+       int r = 1;
        unsigned long flags;
        uint32_t hash = hash_key(prison, key);
-       struct cell *uninitialized_var(cell), *cell2 = NULL;
+       struct cell *cell, *cell2;
 
        BUG_ON(hash > prison->nr_buckets);
 
        spin_lock_irqsave(&prison->lock, flags);
+
        cell = __search_bucket(prison->cells + hash, key);
+       if (cell) {
+               bio_list_add(&cell->bios, inmate);
+               goto out;
+       }
 
-       if (!cell) {
-               /*
-                * Allocate a new cell
-                */
-               spin_unlock_irqrestore(&prison->lock, flags);
-               cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
-               spin_lock_irqsave(&prison->lock, flags);
+       /*
+        * Allocate a new cell
+        */
+       spin_unlock_irqrestore(&prison->lock, flags);
+       cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO);
+       spin_lock_irqsave(&prison->lock, flags);
 
-               /*
-                * We've been unlocked, so we have to double check that
-                * nobody else has inserted this cell in the meantime.
-                */
-               cell = __search_bucket(prison->cells + hash, key);
+       /*
+        * We've been unlocked, so we have to double check that
+        * nobody else has inserted this cell in the meantime.
+        */
+       cell = __search_bucket(prison->cells + hash, key);
+       if (cell) {
+               mempool_free(cell2, prison->cell_pool);
+               bio_list_add(&cell->bios, inmate);
+               goto out;
+       }
+
+       /*
+        * Use new cell.
+        */
+       cell = cell2;
 
-               if (!cell) {
-                       cell = cell2;
-                       cell2 = NULL;
+       cell->prison = prison;
+       memcpy(&cell->key, key, sizeof(cell->key));
+       cell->holder = inmate;
+       bio_list_init(&cell->bios);
+       hlist_add_head(&cell->list, prison->cells + hash);
 
-                       cell->prison = prison;
-                       memcpy(&cell->key, key, sizeof(cell->key));
-                       cell->count = 0;
-                       bio_list_init(&cell->bios);
-                       hlist_add_head(&cell->list, prison->cells + hash);
-               }
-       }
+       r = 0;
 
-       r = cell->count++;
-       bio_list_add(&cell->bios, inmate);
+out:
        spin_unlock_irqrestore(&prison->lock, flags);
 
-       if (cell2)
-               mempool_free(cell2, prison->cell_pool);
-
        *ref = cell;
 
        return r;
@@ -283,8 +288,8 @@ static void __cell_release(struct cell *cell, struct bio_list *inmates)
 
        hlist_del(&cell->list);
 
-       if (inmates)
-               bio_list_merge(inmates, &cell->bios);
+       bio_list_add(inmates, cell->holder);
+       bio_list_merge(inmates, &cell->bios);
 
        mempool_free(cell, prison->cell_pool);
 }
@@ -305,22 +310,44 @@ static void cell_release(struct cell *cell, struct bio_list *bios)
  * bio may be in the cell.  This function releases the cell, and also does
  * a sanity check.
  */
+static void __cell_release_singleton(struct cell *cell, struct bio *bio)
+{
+       hlist_del(&cell->list);
+       BUG_ON(cell->holder != bio);
+       BUG_ON(!bio_list_empty(&cell->bios));
+}
+
 static void cell_release_singleton(struct cell *cell, struct bio *bio)
 {
-       struct bio_prison *prison = cell->prison;
-       struct bio_list bios;
-       struct bio *b;
        unsigned long flags;
-
-       bio_list_init(&bios);
+       struct bio_prison *prison = cell->prison;
 
        spin_lock_irqsave(&prison->lock, flags);
-       __cell_release(cell, &bios);
+       __cell_release_singleton(cell, bio);
        spin_unlock_irqrestore(&prison->lock, flags);
+}
+
+/*
+ * Sometimes we don't want the holder, just the additional bios.
+ */
+static void __cell_release_no_holder(struct cell *cell, struct bio_list *inmates)
+{
+       struct bio_prison *prison = cell->prison;
 
-       b = bio_list_pop(&bios);
-       BUG_ON(b != bio);
-       BUG_ON(!bio_list_empty(&bios));
+       hlist_del(&cell->list);
+       bio_list_merge(inmates, &cell->bios);
+
+       mempool_free(cell, prison->cell_pool);
+}
+
+static void cell_release_no_holder(struct cell *cell, struct bio_list *inmates)
+{
+       unsigned long flags;
+       struct bio_prison *prison = cell->prison;
+
+       spin_lock_irqsave(&prison->lock, flags);
+       __cell_release_no_holder(cell, inmates);
+       spin_unlock_irqrestore(&prison->lock, flags);
 }
 
 static void cell_error(struct cell *cell)
@@ -800,21 +827,16 @@ static void cell_defer(struct thin_c *tc, struct cell *cell,
  * Same as cell_defer above, except it omits one particular detainee,
  * a write bio that covers the block and has already been processed.
  */
-static void cell_defer_except(struct thin_c *tc, struct cell *cell,
-                             struct bio *exception)
+static void cell_defer_except(struct thin_c *tc, struct cell *cell)
 {
        struct bio_list bios;
-       struct bio *bio;
        struct pool *pool = tc->pool;
        unsigned long flags;
 
        bio_list_init(&bios);
-       cell_release(cell, &bios);
 
        spin_lock_irqsave(&pool->lock, flags);
-       while ((bio = bio_list_pop(&bios)))
-               if (bio != exception)
-                       bio_list_add(&pool->deferred_bios, bio);
+       cell_release_no_holder(cell, &pool->deferred_bios);
        spin_unlock_irqrestore(&pool->lock, flags);
 
        wake_worker(pool);
@@ -854,7 +876,7 @@ static void process_prepared_mapping(struct new_mapping *m)
         * the bios in the cell.
         */
        if (bio) {
-               cell_defer_except(tc, m->cell, bio);
+               cell_defer_except(tc, m->cell);
                bio_endio(bio, 0);
        } else
                cell_defer(tc, m->cell, m->data_block);