diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -232,6 +232,7 @@ static struct region *__rh_alloc(struct { struct region *reg, *nreg; +retry: read_unlock(&rh->hash_lock); nreg = mempool_alloc(rh->region_pool, GFP_NOIO); nreg->state = rh->log->type->in_sync(rh->log, region, 1) ? @@ -252,15 +253,19 @@ static struct region *__rh_alloc(struct else { __rh_insert(rh, nreg); - if (nreg->state == RH_CLEAN) { - spin_lock(&rh->region_lock); - list_add(&nreg->list, &rh->clean_regions); - spin_unlock(&rh->region_lock); - } reg = nreg; } write_unlock_irq(&rh->hash_lock); read_lock(&rh->hash_lock); + /* rare, but it's possible the region is freed by other cpu */ + if (reg != __rh_lookup(rh, region)) + goto retry; + if (reg->state == RH_CLEAN) { + spin_lock_irq(&rh->region_lock); + if (!atomic_read(®->pending) && list_empty(®->list)) + list_add(®->list, &rh->clean_regions); + spin_unlock_irq(&rh->region_lock); + } return reg; }