__find_pending_exception drops the snapshot lock for the time of allocation (this is correct and required behavior because the lock is held when freeing the exception into the mempool) After __find_pending_exception regains the lock, it checks if the exception was already placed into &s->pending hash and it checks if the snapshot was already invalidated. But __find_pending_exception doesn't check if the exception was already completed and placed into &s->complete hash. If the process waiting in __find_pending_exception was delayed at this point because of a scheduling latency and the exception was meanwhile completed, __find_pending_exception will miss that and allocate another pending exception for already completed chunk. It will lead to a situation when two records for the same chunk exist and it could lead to data corruption because multiple snapshot I/Os to the affected chunk could be redirected to different locations in the snapshot. Signed-off-by: Mikulas Patocka --- drivers/md/dm-snap.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) Index: linux-2.6.28-devel/drivers/md/dm-snap.c =================================================================== --- linux-2.6.28-devel.orig/drivers/md/dm-snap.c 2009-02-20 15:54:55.000000000 +0100 +++ linux-2.6.28-devel/drivers/md/dm-snap.c 2009-02-20 15:55:09.000000000 +0100 @@ -948,6 +948,10 @@ static void start_copy(struct dm_snap_pe * for this chunk, otherwise it allocates a new one and inserts * it into the pending table. * + * Returns: pointer to a pending exception + * ERR_PTR(-ENOMEM) pointer --- the snapshot is invalidated + * NULL --- the exception was already completed, the caller should recheck + * * NOTE: a write lock must be held on snap->lock before calling * this. */ @@ -981,6 +985,12 @@ __find_pending_exception(struct dm_snaps return ERR_PTR(-ENOMEM); } + e = lookup_exception(&s->complete, chunk); + if (e) { + free_pending_exception(pe); + return NULL; + } + e = lookup_exception(&s->pending, chunk); if (e) { free_pending_exception(pe); @@ -1041,6 +1051,7 @@ static int snapshot_map(struct dm_target goto out_unlock; } +lookup_again: /* If the block is already remapped - use that, else remap it */ e = lookup_exception(&s->complete, chunk); if (e) { @@ -1055,6 +1066,8 @@ static int snapshot_map(struct dm_target */ if (bio_rw(bio) == WRITE) { pe = __find_pending_exception(s, bio); + if (!pe) + goto lookup_again; if (IS_ERR(pe)) { __invalidate_snapshot(s, PTR_ERR(pe)); r = -EIO; @@ -1189,6 +1202,8 @@ static int __origin_write(struct list_he goto next_snapshot; pe = __find_pending_exception(snap, bio); + if (!pe) + goto next_snapshot; if (IS_ERR(pe)) { __invalidate_snapshot(snap, PTR_ERR(pe)); goto next_snapshot;