diff --git a/drivers/md/dm-bow.c b/drivers/md/dm-bow.c index e12b8f986686..6ce39fac0471 100644 --- a/drivers/md/dm-bow.c +++ b/drivers/md/dm-bow.c @@ -87,6 +87,7 @@ struct bow_context { struct mutex ranges_lock; /* Hold to access this struct and/or ranges */ struct rb_root ranges; struct dm_kobject_holder kobj_holder; /* for sysfs attributes */ + struct mutex state_lock; atomic_t state; /* One of the enum state values above */ u64 trims_total; struct log_sector *log_sector; @@ -526,6 +527,12 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, return -EINVAL; } + /* Block new writes until state change is complete. */ + mutex_lock(&bc->state_lock); + + /* Flush any already-queued writes before the state change. */ + flush_workqueue(bc->workqueue); + mutex_lock(&bc->ranges_lock); original_state = atomic_read(&bc->state); if (state != original_state + 1) { @@ -561,6 +568,8 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, bad: mutex_unlock(&bc->ranges_lock); + mutex_unlock(&bc->state_lock); + return ret; } @@ -736,6 +745,7 @@ static int dm_bow_ctr(struct dm_target *ti, unsigned int argc, char **argv) } init_completion(&bc->kobj_holder.completion); + mutex_init(&bc->state_lock); mutex_init(&bc->ranges_lock); bc->ranges = RB_ROOT; bc->bufio = dm_bufio_client_create(bc->dev->bdev, bc->block_size, 1, 0, @@ -1121,6 +1131,8 @@ static int dm_bow_map(struct dm_target *ti, struct bio *bio) int ret = DM_MAPIO_REMAPPED; struct bow_context *bc = ti->private; + /* Fast path when already committed or when performing a read. */ + if (likely(bc->state.counter == COMMITTED)) return remap_unless_illegal_trim(bc, bio); @@ -1130,6 +1142,13 @@ static int dm_bow_map(struct dm_target *ti, struct bio *bio) if (bio->bi_iter.bi_size == 0) return remap_unless_illegal_trim(bc, bio); + /* + * Fall back to the slower path when we may be in TRIM/CHECKPOINT. + * Operations must wait for any pending state changes to complete. + */ + + mutex_lock(&bc->state_lock); + if (atomic_read(&bc->state) != COMMITTED) { enum state state; @@ -1152,6 +1171,8 @@ static int dm_bow_map(struct dm_target *ti, struct bio *bio) mutex_unlock(&bc->ranges_lock); } + mutex_unlock(&bc->state_lock); + if (ret == DM_MAPIO_REMAPPED) return remap_unless_illegal_trim(bc, bio);