diff -purN linux-2.6.9-11.34.EL-04/drivers/md/dm-raid1.c linux-2.6.9-11.34.EL-05/drivers/md/dm-raid1.c --- linux-2.6.9-11.34.EL-04/drivers/md/dm-raid1.c 2005-07-29 13:22:45.000000000 -0500 +++ linux-2.6.9-11.34.EL-05/drivers/md/dm-raid1.c 2005-07-29 15:17:29.725740024 -0500 @@ -231,12 +231,18 @@ static void __rh_insert(struct region_ha list_add(®->hash_list, rh->buckets + h); } +/* __rh_alloc + * @rh + * @region + * + * The region_hash hash_lock _must_ be held write_lock_irq. + * Never call this directly - use __rh_find + */ static struct region *__rh_alloc(struct region_hash *rh, region_t region) { - struct region *reg, *nreg; + struct region *nreg; - read_unlock(&rh->hash_lock); - nreg = mempool_alloc(rh->region_pool, GFP_NOIO); + nreg = mempool_alloc(rh->region_pool, GFP_ATOMIC); nreg->state = rh->log->type->in_sync(rh->log, region, 1) ? RH_CLEAN : RH_NOSYNC; nreg->rh = rh; @@ -246,34 +252,33 @@ static struct region *__rh_alloc(struct atomic_set(&nreg->pending, 0); bio_list_init(&nreg->delayed_bios); - write_lock_irq(&rh->hash_lock); - - reg = __rh_lookup(rh, region); - if (reg) - /* we lost the race */ - mempool_free(nreg, rh->region_pool); - 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; + __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); } - write_unlock_irq(&rh->hash_lock); - read_lock(&rh->hash_lock); - return reg; + return nreg; } -static inline struct region *__rh_find(struct region_hash *rh, region_t region) +/* __rh_find + * @rh + * @region + * @rw: either READ or WRITE + * + * The region_hash hash_lock must be held in one of two states + * read_lock(): perform lookup only + * write_lock_irq(): perform lookup and possible allocation + */ +static inline struct region *__rh_find(struct region_hash *rh, + region_t region, int rw) { struct region *reg; reg = __rh_lookup(rh, region); - if (!reg) + if (!reg && (rw == WRITE)) reg = __rh_alloc(rh, region); return reg; @@ -374,10 +379,21 @@ static void rh_update_states(struct regi static void rh_inc(struct region_hash *rh, region_t region) { + int rw = READ; struct region *reg; read_lock(&rh->hash_lock); - reg = __rh_find(rh, region); + reg = __rh_find(rh, region, rw); + if (!reg) { + read_unlock(&rh->hash_lock); + rw = WRITE; + write_lock_irq(&rh->hash_lock); + reg = __rh_find(rh, region, rw); + } + + spin_lock_irq(&rh->region_lock); + atomic_inc(®->pending); + spin_unlock_irq(&rh->region_lock); if (reg->state == RH_CLEAN) { rh->log->type->mark_region(rh->log, reg->key); @@ -387,8 +403,10 @@ static void rh_inc(struct region_hash *r spin_unlock_irq(&rh->region_lock); } - atomic_inc(®->pending); - read_unlock(&rh->hash_lock); + if (rw == WRITE) + write_unlock_irq(&rh->hash_lock); + else + read_unlock(&rh->hash_lock); } static void rh_inc_pending(struct region_hash *rh, struct bio_list *bios) @@ -409,17 +427,17 @@ static void rh_dec(struct region_hash *r reg = __rh_lookup(rh, region); read_unlock(&rh->hash_lock); + spin_lock_irqsave(&rh->region_lock, flags); if (atomic_dec_and_test(®->pending)) { - spin_lock_irqsave(&rh->region_lock, flags); if (reg->state == RH_RECOVERING) { list_add_tail(®->list, &rh->quiesced_regions); } else { reg->state = RH_CLEAN; list_add(®->list, &rh->clean_regions); } - spin_unlock_irqrestore(&rh->region_lock, flags); should_wake = 1; } + spin_unlock_irqrestore(&rh->region_lock, flags); if (should_wake) wake(); @@ -446,8 +464,15 @@ static int __rh_recovery_prepare(struct * recovering flag. */ read_lock(&rh->hash_lock); - reg = __rh_find(rh, region); - read_unlock(&rh->hash_lock); + reg = __rh_find(rh, region, READ); + if (reg) + read_unlock(&rh->hash_lock); + else { + read_unlock(&rh->hash_lock); + write_lock_irq(&rh->hash_lock); + reg = __rh_find(rh, region, WRITE); + write_unlock_irq(&rh->hash_lock); + } spin_lock_irq(&rh->region_lock); reg->state = RH_RECOVERING; @@ -511,12 +536,23 @@ static void rh_flush(struct region_hash static void rh_delay(struct region_hash *rh, struct bio *bio) { + int rw = READ; struct region *reg; read_lock(&rh->hash_lock); - reg = __rh_find(rh, bio_to_region(rh, bio)); + reg = __rh_find(rh, bio_to_region(rh, bio), rw); + if (!reg) { + read_unlock(&rh->hash_lock); + rw = WRITE; + write_lock_irq(&rh->hash_lock); + reg = __rh_find(rh, bio_to_region(rh, bio), rw); + } bio_list_add(®->delayed_bios, bio); - read_unlock(&rh->hash_lock); + + if (rw == WRITE) + write_unlock(&rh->hash_lock); + else + read_unlock(&rh->hash_lock); } static void rh_stop_recovery(struct region_hash *rh)