Fix problems with raid1 resync code Fix bugs discovered by Paul.Clements@steeleye.com. Routines were called while spinlocks were held which shouldn't have been called in that situation (raid1_grow_buffers, close_sync). At end of resync, start_future (sectors) was set to sb->size+1 (K) in instead of (sb->size<<1)+1. ----------- Diffstat output ------------ ./drivers/md/raid1.c | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 40 insertions(+), 14 deletions(-) --- ./drivers/md/raid1.c 2002/03/25 21:53:59 1.1 +++ ./drivers/md/raid1.c 2002/03/26 01:47:56 1.2 @@ -269,8 +269,9 @@ static int raid1_grow_buffers (raid1_conf_t *conf, int cnt) { int i = 0; + struct raid1_bh *head = NULL, **tail; + tail = &head; - md_spin_lock_irq(&conf->device_lock); while (i < cnt) { struct raid1_bh *r1_bh; struct page *page; @@ -287,10 +288,18 @@ memset(r1_bh, 0, sizeof(*r1_bh)); r1_bh->bh_req.b_page = page; r1_bh->bh_req.b_data = page_address(page); - r1_bh->next_r1 = conf->freebuf; - conf->freebuf = r1_bh; + *tail = r1_bh; + r1_bh->next_r1 = NULL; + tail = & r1_bh->next_r1; i++; } + /* this lock probably isn't needed, as at the time when + * we are allocating buffers, nobody else will be touching the + * freebuf list. But it doesn't hurt.... + */ + md_spin_lock_irq(&conf->device_lock); + *tail = conf->freebuf; + conf->freebuf = head; md_spin_unlock_irq(&conf->device_lock); return i; } @@ -825,7 +834,7 @@ conf->start_ready = conf->start_pending; wait_event_lock_irq(conf->wait_ready, !conf->cnt_pending, conf->segment_lock); conf->start_active =conf->start_ready = conf->start_pending = conf->start_future; - conf->start_future = mddev->sb->size+1; + conf->start_future = (mddev->sb->size<<1)+1; conf->cnt_pending = conf->cnt_future; conf->cnt_future = 0; conf->phase = conf->phase ^1; @@ -849,6 +858,14 @@ mdk_rdev_t *spare_rdev, *failed_rdev; print_raid1_conf(conf); + + switch (state) { + case DISKOP_SPARE_ACTIVE: + case DISKOP_SPARE_INACTIVE: + /* need to wait for pending sync io before locking device */ + close_sync(conf); + } + md_spin_lock_irq(&conf->device_lock); /* * find the disk ... @@ -951,7 +968,11 @@ * Deactivate a spare disk: */ case DISKOP_SPARE_INACTIVE: - close_sync(conf); + if (conf->start_future > 0) { + MD_BUG(); + err = -EBUSY; + break; + } sdisk = conf->mirrors + spare_disk; sdisk->operational = 0; sdisk->write_only = 0; @@ -964,7 +985,11 @@ * property) */ case DISKOP_SPARE_ACTIVE: - close_sync(conf); + if (conf->start_future > 0) { + MD_BUG(); + err = -EBUSY; + break; + } sdisk = conf->mirrors + spare_disk; fdisk = conf->mirrors + failed_disk; @@ -1328,23 +1353,25 @@ int bsize; int disk; int block_nr; + int buffs; + if (!sector_nr) { + /* we want enough buffers to hold twice the window of 128*/ + buffs = 128 *2 / (PAGE_SIZE>>9); + buffs = raid1_grow_buffers(conf, buffs); + if (buffs < 2) + goto nomem; + conf->window = buffs*(PAGE_SIZE>>9)/2; + } spin_lock_irq(&conf->segment_lock); if (!sector_nr) { /* initialize ...*/ - int buffs; conf->start_active = 0; conf->start_ready = 0; conf->start_pending = 0; conf->start_future = 0; conf->phase = 0; - /* we want enough buffers to hold twice the window of 128*/ - buffs = 128 *2 / (PAGE_SIZE>>9); - buffs = raid1_grow_buffers(conf, buffs); - if (buffs < 2) - goto nomem; - conf->window = buffs*(PAGE_SIZE>>9)/2; conf->cnt_future += conf->cnt_done+conf->cnt_pending; conf->cnt_done = conf->cnt_pending = 0; if (conf->cnt_ready || conf->cnt_active) @@ -1429,7 +1456,6 @@ nomem: raid1_shrink_buffers(conf); - spin_unlock_irq(&conf->segment_lock); return -ENOMEM; }