}
bio_list_init(&rbio->bio_list);
- init_waitqueue_head(&rbio->io_wait);
+ init_completion(&rbio->io_done);
INIT_LIST_HEAD(&rbio->plug_list);
spin_lock_init(&rbio->bio_list_lock);
INIT_LIST_HEAD(&rbio->stripe_cache);
}
}
+/*
+ * Begin a submission round of @nr bios whose end_io functions report through
+ * rbio_dec_io(). stripes_pending gets one extra count on behalf of the
+ * submitter, dropped by rbio_wait_io(): this guarantees exactly one
+ * completion per round even when @nr is 0 or every bio finishes before the
+ * submitter starts waiting.
+ *
+ * The completion is what makes the round safe against a use-after-free the
+ * old bare waitqueue had: once the final bio's decrement made
+ * stripes_pending visible as zero, the waiter could pass its wait_event()
+ * condition check and free the rbio while the bio's end_io context was still
+ * inside wake_up() on the rbio's embedded waitqueue lock. With a
+ * completion, the waiter cannot return before complete() has released the
+ * completion's lock, so the completing context is finished with the rbio.
+ */
+static void rbio_begin_io(struct btrfs_raid_bio *rbio, unsigned int nr)
+{
+ reinit_completion(&rbio->io_done);
+ atomic_set(&rbio->stripes_pending, nr + 1);
+}
+
+static void rbio_dec_io(struct btrfs_raid_bio *rbio)
+{
+ if (atomic_dec_and_test(&rbio->stripes_pending))
+ complete(&rbio->io_done);
+}
+
+static void rbio_wait_io(struct btrfs_raid_bio *rbio)
+{
+ /* Drop the submitter's count from rbio_begin_io(). */
+ rbio_dec_io(rbio);
+ wait_for_completion(&rbio->io_done);
+}
+
static void raid_wait_read_end_io(struct bio *bio)
{
struct btrfs_raid_bio *rbio = bio->bi_private;
}
bio_put(bio);
- if (atomic_dec_and_test(&rbio->stripes_pending))
- wake_up(&rbio->io_wait);
+ rbio_dec_io(rbio);
}
static void submit_read_wait_bio_list(struct btrfs_raid_bio *rbio,
{
struct bio *bio;
- atomic_set(&rbio->stripes_pending, bio_list_size(bio_list));
+ rbio_begin_io(rbio, bio_list_size(bio_list));
while ((bio = bio_list_pop(bio_list))) {
bio->bi_end_io = raid_wait_read_end_io;
submit_bio(bio);
}
- wait_event(rbio->io_wait, atomic_read(&rbio->stripes_pending) == 0);
+ rbio_wait_io(rbio);
}
static int alloc_rbio_data_pages(struct btrfs_raid_bio *rbio)
if (bio->bi_status)
rbio_update_error_bitmap(rbio, bio);
bio_put(bio);
- if (atomic_dec_and_test(&rbio->stripes_pending))
- wake_up(&rbio->io_wait);
+ rbio_dec_io(rbio);
}
static void submit_write_bios(struct btrfs_raid_bio *rbio,
{
struct bio *bio;
- atomic_set(&rbio->stripes_pending, bio_list_size(bio_list));
+ rbio_begin_io(rbio, bio_list_size(bio_list));
while ((bio = bio_list_pop(bio_list))) {
bio->bi_end_io = raid_wait_write_end_io;
/* We should have at least one bio assembled. */
ASSERT(bio_list_size(&bio_list));
submit_write_bios(rbio, &bio_list);
- wait_event(rbio->io_wait, atomic_read(&rbio->stripes_pending) == 0);
+ rbio_wait_io(rbio);
/* We may have more errors than our tolerance during the read. */
for (sectornr = 0; sectornr < rbio->stripe_nsectors; sectornr++) {
* and writeback the good content.
*/
ret = finish_parity_scrub(rbio);
- wait_event(rbio->io_wait, atomic_read(&rbio->stripes_pending) == 0);
+ /* A submission round only began if finish_parity_scrub() succeeded. */
+ if (ret == 0)
+ rbio_wait_io(rbio);
for (sector_nr = 0; sector_nr < rbio->stripe_nsectors; sector_nr++) {
int found_errors;
#include <linux/bio.h>
#include <linux/refcount.h>
#include <linux/workqueue.h>
+#include <linux/completion.h>
#include "volumes.h"
struct page;
refcount_t refs;
+ /*
+ * In-flight bios of the current submission round, plus one bias
+ * count held by the submitter and dropped when it starts waiting.
+ * The final decrement completes io_done; a completion (unlike a
+ * bare waitqueue) guarantees the completing context is finished
+ * with the rbio before the waiter can return and free it.
+ */
atomic_t stripes_pending;
- wait_queue_head_t io_wait;
+ struct completion io_done;
/* Bitmap to record which horizontal stripe has data */
unsigned long dbitmap;