dm bio prison: add dm_cell_promote_or_release()
authorJoe Thornber <ejt@redhat.com>
Fri, 15 May 2015 14:23:35 +0000 (15:23 +0100)
committerMike Snitzer <snitzer@redhat.com>
Fri, 29 May 2015 18:19:06 +0000 (14:19 -0400)
Rather than always releasing the prisoners in a cell, the client may
want to promote one of them to be the new holder.  There is a race here
though between releasing an empty cell, and other threads adding new
inmates.  So this function makes the decision with its lock held.

This function can have two outcomes:
i)  An inmate is promoted to be the holder of the cell (return value of 0).
ii) The cell has no inmate for promotion and is released (return value of 1).

Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
drivers/md/dm-bio-prison.c
drivers/md/dm-bio-prison.h

index be065300e93c8d16d2a5cc5c7753224932e27836..cd6d1d21e0570097975030ccb42c315dcf825fd2 100644 (file)
@@ -255,6 +255,32 @@ void dm_cell_visit_release(struct dm_bio_prison *prison,
 }
 EXPORT_SYMBOL_GPL(dm_cell_visit_release);
 
+static int __promote_or_release(struct dm_bio_prison *prison,
+                               struct dm_bio_prison_cell *cell)
+{
+       if (bio_list_empty(&cell->bios)) {
+               rb_erase(&cell->node, &prison->cells);
+               return 1;
+       }
+
+       cell->holder = bio_list_pop(&cell->bios);
+       return 0;
+}
+
+int dm_cell_promote_or_release(struct dm_bio_prison *prison,
+                              struct dm_bio_prison_cell *cell)
+{
+       int r;
+       unsigned long flags;
+
+       spin_lock_irqsave(&prison->lock, flags);
+       r = __promote_or_release(prison, cell);
+       spin_unlock_irqrestore(&prison->lock, flags);
+
+       return r;
+}
+EXPORT_SYMBOL_GPL(dm_cell_promote_or_release);
+
 /*----------------------------------------------------------------*/
 
 #define DEFERRED_SET_SIZE 64
index 74cf01144b1fc9c12777f669e1cf6e5e23422d44..54352f009bfd5ad3cf011896536c4aa5baee18c6 100644 (file)
@@ -101,6 +101,19 @@ void dm_cell_visit_release(struct dm_bio_prison *prison,
                           void (*visit_fn)(void *, struct dm_bio_prison_cell *),
                           void *context, struct dm_bio_prison_cell *cell);
 
+/*
+ * Rather than always releasing the prisoners in a cell, the client may
+ * want to promote one of them to be the new holder.  There is a race here
+ * though between releasing an empty cell, and other threads adding new
+ * inmates.  So this function makes the decision with its lock held.
+ *
+ * This function can have two outcomes:
+ * i) An inmate is promoted to be the holder of the cell (return value of 0).
+ * ii) The cell has no inmate for promotion and is released (return value of 1).
+ */
+int dm_cell_promote_or_release(struct dm_bio_prison *prison,
+                              struct dm_bio_prison_cell *cell);
+
 /*----------------------------------------------------------------*/
 
 /*