Commit 5d8e7fb6 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'md/3.20' of git://neil.brown.name/md

Pull md updates from Neil Brown:

 - assorted locking changes so that access to /proc/mdstat
   and much of /sys/block/mdXX/md/* is protected by a spinlock
   rather than a mutex and will never block indefinitely.

 - Make an 'if' condition in RAID5 - which has been implicated
   in recent bugs - more readable.

 - misc minor fixes

* tag 'md/3.20' of git://neil.brown.name/md: (28 commits)
  md/raid10: fix conversion from RAID0 to RAID10
  md: wakeup thread upon rdev_dec_pending()
  md: make reconfig_mutex optional for writes to md sysfs files.
  md: move mddev_lock and related to md.h
  md: use mddev->lock to protect updates to resync_{min,max}.
  md: minor cleanup in safe_delay_store.
  md: move GET_BITMAP_FILE ioctl out from mddev_lock.
  md: tidy up set_bitmap_file
  md: remove unnecessary 'buf' from get_bitmap_file.
  md: remove mddev_lock from rdev_attr_show()
  md: remove mddev_lock() from md_attr_show()
  md/raid5: use ->lock to protect accessing raid5 sysfs attributes.
  md: remove need for mddev_lock() in md_seq_show()
  md/bitmap: protect clearing of ->bitmap by mddev->lock
  md: protect ->pers changes with mddev->lock
  md: level_store: group all important changes into one place.
  md: rename ->stop to ->free
  md: split detach operation out from ->stop.
  md/linear: remove rcu protections in favour of suspend/resume
  md: make merge_bvec_fn more robust in face of personality changes.
  ...
parents 87c9172f 53a6ab4d
...@@ -148,6 +148,7 @@ cfi-sections := $(call as-instr,.cfi_sections .debug_frame,-DCONFIG_AS_CFI_SECTI ...@@ -148,6 +148,7 @@ cfi-sections := $(call as-instr,.cfi_sections .debug_frame,-DCONFIG_AS_CFI_SECTI
# does binutils support specific instructions? # does binutils support specific instructions?
asinstr := $(call as-instr,fxsaveq (%rax),-DCONFIG_AS_FXSAVEQ=1) asinstr := $(call as-instr,fxsaveq (%rax),-DCONFIG_AS_FXSAVEQ=1)
asinstr += $(call as-instr,pshufb %xmm0$(comma)%xmm0,-DCONFIG_AS_SSSE3=1)
asinstr += $(call as-instr,crc32l %eax$(comma)%eax,-DCONFIG_AS_CRC32=1) asinstr += $(call as-instr,crc32l %eax$(comma)%eax,-DCONFIG_AS_CRC32=1)
avx_instr := $(call as-instr,vxorps %ymm0$(comma)%ymm1$(comma)%ymm2,-DCONFIG_AS_AVX=1) avx_instr := $(call as-instr,vxorps %ymm0$(comma)%ymm1$(comma)%ymm2,-DCONFIG_AS_AVX=1)
avx2_instr :=$(call as-instr,vpbroadcastb %xmm0$(comma)%ymm1,-DCONFIG_AS_AVX2=1) avx2_instr :=$(call as-instr,vpbroadcastb %xmm0$(comma)%ymm1,-DCONFIG_AS_AVX2=1)
......
...@@ -1619,7 +1619,9 @@ void bitmap_destroy(struct mddev *mddev) ...@@ -1619,7 +1619,9 @@ void bitmap_destroy(struct mddev *mddev)
return; return;
mutex_lock(&mddev->bitmap_info.mutex); mutex_lock(&mddev->bitmap_info.mutex);
spin_lock(&mddev->lock);
mddev->bitmap = NULL; /* disconnect from the md device */ mddev->bitmap = NULL; /* disconnect from the md device */
spin_unlock(&mddev->lock);
mutex_unlock(&mddev->bitmap_info.mutex); mutex_unlock(&mddev->bitmap_info.mutex);
if (mddev->thread) if (mddev->thread)
mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
...@@ -2209,11 +2211,13 @@ __ATTR(metadata, S_IRUGO|S_IWUSR, metadata_show, metadata_store); ...@@ -2209,11 +2211,13 @@ __ATTR(metadata, S_IRUGO|S_IWUSR, metadata_show, metadata_store);
static ssize_t can_clear_show(struct mddev *mddev, char *page) static ssize_t can_clear_show(struct mddev *mddev, char *page)
{ {
int len; int len;
spin_lock(&mddev->lock);
if (mddev->bitmap) if (mddev->bitmap)
len = sprintf(page, "%s\n", (mddev->bitmap->need_sync ? len = sprintf(page, "%s\n", (mddev->bitmap->need_sync ?
"false" : "true")); "false" : "true"));
else else
len = sprintf(page, "\n"); len = sprintf(page, "\n");
spin_unlock(&mddev->lock);
return len; return len;
} }
...@@ -2238,10 +2242,15 @@ __ATTR(can_clear, S_IRUGO|S_IWUSR, can_clear_show, can_clear_store); ...@@ -2238,10 +2242,15 @@ __ATTR(can_clear, S_IRUGO|S_IWUSR, can_clear_show, can_clear_store);
static ssize_t static ssize_t
behind_writes_used_show(struct mddev *mddev, char *page) behind_writes_used_show(struct mddev *mddev, char *page)
{ {
ssize_t ret;
spin_lock(&mddev->lock);
if (mddev->bitmap == NULL) if (mddev->bitmap == NULL)
return sprintf(page, "0\n"); ret = sprintf(page, "0\n");
return sprintf(page, "%lu\n", else
mddev->bitmap->behind_writes_used); ret = sprintf(page, "%lu\n",
mddev->bitmap->behind_writes_used);
spin_unlock(&mddev->lock);
return ret;
} }
static ssize_t static ssize_t
......
...@@ -746,13 +746,7 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits) ...@@ -746,13 +746,7 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
{ {
struct raid_set *rs = container_of(cb, struct raid_set, callbacks); struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
if (rs->raid_type->level == 1) return mddev_congested(&rs->md, bits);
return md_raid1_congested(&rs->md, bits);
if (rs->raid_type->level == 10)
return md_raid10_congested(&rs->md, bits);
return md_raid5_congested(&rs->md, bits);
} }
/* /*
......
...@@ -332,13 +332,11 @@ static int run(struct mddev *mddev) ...@@ -332,13 +332,11 @@ static int run(struct mddev *mddev)
return 0; return 0;
} }
static int stop(struct mddev *mddev) static void faulty_free(struct mddev *mddev, void *priv)
{ {
struct faulty_conf *conf = mddev->private; struct faulty_conf *conf = priv;
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
static struct md_personality faulty_personality = static struct md_personality faulty_personality =
...@@ -348,7 +346,7 @@ static struct md_personality faulty_personality = ...@@ -348,7 +346,7 @@ static struct md_personality faulty_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = faulty_free,
.status = status, .status = status,
.check_reshape = reshape, .check_reshape = reshape,
.size = faulty_size, .size = faulty_size,
......
...@@ -34,7 +34,7 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) ...@@ -34,7 +34,7 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
lo = 0; lo = 0;
hi = mddev->raid_disks - 1; hi = mddev->raid_disks - 1;
conf = rcu_dereference(mddev->private); conf = mddev->private;
/* /*
* Binary Search * Binary Search
...@@ -60,18 +60,16 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) ...@@ -60,18 +60,16 @@ static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector)
* *
* Return amount of bytes we can take at this offset * Return amount of bytes we can take at this offset
*/ */
static int linear_mergeable_bvec(struct request_queue *q, static int linear_mergeable_bvec(struct mddev *mddev,
struct bvec_merge_data *bvm, struct bvec_merge_data *bvm,
struct bio_vec *biovec) struct bio_vec *biovec)
{ {
struct mddev *mddev = q->queuedata;
struct dev_info *dev0; struct dev_info *dev0;
unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9; unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int maxbytes = biovec->bv_len; int maxbytes = biovec->bv_len;
struct request_queue *subq; struct request_queue *subq;
rcu_read_lock();
dev0 = which_dev(mddev, sector); dev0 = which_dev(mddev, sector);
maxsectors = dev0->end_sector - sector; maxsectors = dev0->end_sector - sector;
subq = bdev_get_queue(dev0->rdev->bdev); subq = bdev_get_queue(dev0->rdev->bdev);
...@@ -81,7 +79,6 @@ static int linear_mergeable_bvec(struct request_queue *q, ...@@ -81,7 +79,6 @@ static int linear_mergeable_bvec(struct request_queue *q,
maxbytes = min(maxbytes, subq->merge_bvec_fn(subq, bvm, maxbytes = min(maxbytes, subq->merge_bvec_fn(subq, bvm,
biovec)); biovec));
} }
rcu_read_unlock();
if (maxsectors < bio_sectors) if (maxsectors < bio_sectors)
maxsectors = 0; maxsectors = 0;
...@@ -97,24 +94,18 @@ static int linear_mergeable_bvec(struct request_queue *q, ...@@ -97,24 +94,18 @@ static int linear_mergeable_bvec(struct request_queue *q,
return maxsectors << 9; return maxsectors << 9;
} }
static int linear_congested(void *data, int bits) static int linear_congested(struct mddev *mddev, int bits)
{ {
struct mddev *mddev = data;
struct linear_conf *conf; struct linear_conf *conf;
int i, ret = 0; int i, ret = 0;
if (mddev_congested(mddev, bits)) conf = mddev->private;
return 1;
rcu_read_lock();
conf = rcu_dereference(mddev->private);
for (i = 0; i < mddev->raid_disks && !ret ; i++) { for (i = 0; i < mddev->raid_disks && !ret ; i++) {
struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev);
ret |= bdi_congested(&q->backing_dev_info, bits); ret |= bdi_congested(&q->backing_dev_info, bits);
} }
rcu_read_unlock();
return ret; return ret;
} }
...@@ -123,12 +114,10 @@ static sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disk ...@@ -123,12 +114,10 @@ static sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disk
struct linear_conf *conf; struct linear_conf *conf;
sector_t array_sectors; sector_t array_sectors;
rcu_read_lock(); conf = mddev->private;
conf = rcu_dereference(mddev->private);
WARN_ONCE(sectors || raid_disks, WARN_ONCE(sectors || raid_disks,
"%s does not support generic reshape\n", __func__); "%s does not support generic reshape\n", __func__);
array_sectors = conf->array_sectors; array_sectors = conf->array_sectors;
rcu_read_unlock();
return array_sectors; return array_sectors;
} }
...@@ -217,10 +206,6 @@ static int linear_run (struct mddev *mddev) ...@@ -217,10 +206,6 @@ static int linear_run (struct mddev *mddev)
mddev->private = conf; mddev->private = conf;
md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
mddev->queue->backing_dev_info.congested_fn = linear_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
ret = md_integrity_register(mddev); ret = md_integrity_register(mddev);
if (ret) { if (ret) {
kfree(conf); kfree(conf);
...@@ -252,38 +237,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) ...@@ -252,38 +237,23 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev)
if (!newconf) if (!newconf)
return -ENOMEM; return -ENOMEM;
oldconf = rcu_dereference_protected(mddev->private, mddev_suspend(mddev);
lockdep_is_held( oldconf = mddev->private;
&mddev->reconfig_mutex));
mddev->raid_disks++; mddev->raid_disks++;
rcu_assign_pointer(mddev->private, newconf); mddev->private = newconf;
md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); md_set_array_sectors(mddev, linear_size(mddev, 0, 0));
set_capacity(mddev->gendisk, mddev->array_sectors); set_capacity(mddev->gendisk, mddev->array_sectors);
mddev_resume(mddev);
revalidate_disk(mddev->gendisk); revalidate_disk(mddev->gendisk);
kfree_rcu(oldconf, rcu); kfree(oldconf);
return 0; return 0;
} }
static int linear_stop (struct mddev *mddev) static void linear_free(struct mddev *mddev, void *priv)
{ {
struct linear_conf *conf = struct linear_conf *conf = priv;
rcu_dereference_protected(mddev->private,
lockdep_is_held(
&mddev->reconfig_mutex));
/*
* We do not require rcu protection here since
* we hold reconfig_mutex for both linear_add and
* linear_stop, so they cannot race.
* We should make sure any old 'conf's are properly
* freed though.
*/
rcu_barrier();
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
static void linear_make_request(struct mddev *mddev, struct bio *bio) static void linear_make_request(struct mddev *mddev, struct bio *bio)
...@@ -299,16 +269,12 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio) ...@@ -299,16 +269,12 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio)
} }
do { do {
rcu_read_lock();
tmp_dev = which_dev(mddev, bio->bi_iter.bi_sector); tmp_dev = which_dev(mddev, bio->bi_iter.bi_sector);
start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors;
end_sector = tmp_dev->end_sector; end_sector = tmp_dev->end_sector;
data_offset = tmp_dev->rdev->data_offset; data_offset = tmp_dev->rdev->data_offset;
bio->bi_bdev = tmp_dev->rdev->bdev; bio->bi_bdev = tmp_dev->rdev->bdev;
rcu_read_unlock();
if (unlikely(bio->bi_iter.bi_sector >= end_sector || if (unlikely(bio->bi_iter.bi_sector >= end_sector ||
bio->bi_iter.bi_sector < start_sector)) bio->bi_iter.bi_sector < start_sector))
goto out_of_bounds; goto out_of_bounds;
...@@ -355,6 +321,10 @@ static void linear_status (struct seq_file *seq, struct mddev *mddev) ...@@ -355,6 +321,10 @@ static void linear_status (struct seq_file *seq, struct mddev *mddev)
seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2); seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2);
} }
static void linear_quiesce(struct mddev *mddev, int state)
{
}
static struct md_personality linear_personality = static struct md_personality linear_personality =
{ {
.name = "linear", .name = "linear",
...@@ -362,10 +332,13 @@ static struct md_personality linear_personality = ...@@ -362,10 +332,13 @@ static struct md_personality linear_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = linear_make_request, .make_request = linear_make_request,
.run = linear_run, .run = linear_run,
.stop = linear_stop, .free = linear_free,
.status = linear_status, .status = linear_status,
.hot_add_disk = linear_add, .hot_add_disk = linear_add,
.size = linear_size, .size = linear_size,
.quiesce = linear_quiesce,
.congested = linear_congested,
.mergeable_bvec = linear_mergeable_bvec,
}; };
static int __init linear_init (void) static int __init linear_init (void)
......
...@@ -72,6 +72,7 @@ static struct workqueue_struct *md_misc_wq; ...@@ -72,6 +72,7 @@ static struct workqueue_struct *md_misc_wq;
static int remove_and_add_spares(struct mddev *mddev, static int remove_and_add_spares(struct mddev *mddev,
struct md_rdev *this); struct md_rdev *this);
static void mddev_detach(struct mddev *mddev);
/* /*
* Default number of read corrections we'll attempt on an rdev * Default number of read corrections we'll attempt on an rdev
...@@ -292,8 +293,8 @@ static void md_make_request(struct request_queue *q, struct bio *bio) ...@@ -292,8 +293,8 @@ static void md_make_request(struct request_queue *q, struct bio *bio)
/* mddev_suspend makes sure no new requests are submitted /* mddev_suspend makes sure no new requests are submitted
* to the device, and that any requests that have been submitted * to the device, and that any requests that have been submitted
* are completely handled. * are completely handled.
* Once ->stop is called and completes, the module will be completely * Once mddev_detach() is called and completes, the module will be
* unused. * completely unused.
*/ */
void mddev_suspend(struct mddev *mddev) void mddev_suspend(struct mddev *mddev)
{ {
...@@ -321,10 +322,47 @@ EXPORT_SYMBOL_GPL(mddev_resume); ...@@ -321,10 +322,47 @@ EXPORT_SYMBOL_GPL(mddev_resume);
int mddev_congested(struct mddev *mddev, int bits) int mddev_congested(struct mddev *mddev, int bits)
{ {
return mddev->suspended; struct md_personality *pers = mddev->pers;
int ret = 0;
rcu_read_lock();
if (mddev->suspended)
ret = 1;
else if (pers && pers->congested)
ret = pers->congested(mddev, bits);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL_GPL(mddev_congested);
static int md_congested(void *data, int bits)
{
struct mddev *mddev = data;
return mddev_congested(mddev, bits);
} }
EXPORT_SYMBOL(mddev_congested);
static int md_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
struct mddev *mddev = q->queuedata;
int ret;
rcu_read_lock();
if (mddev->suspended) {
/* Must always allow one vec */
if (bvm->bi_size == 0)
ret = biovec->bv_len;
else
ret = 0;
} else {
struct md_personality *pers = mddev->pers;
if (pers && pers->mergeable_bvec)
ret = pers->mergeable_bvec(mddev, bvm, biovec);
else
ret = biovec->bv_len;
}
rcu_read_unlock();
return ret;
}
/* /*
* Generic flush handling for md * Generic flush handling for md
*/ */
...@@ -397,12 +435,12 @@ static void md_submit_flush_data(struct work_struct *ws) ...@@ -397,12 +435,12 @@ static void md_submit_flush_data(struct work_struct *ws)
void md_flush_request(struct mddev *mddev, struct bio *bio) void md_flush_request(struct mddev *mddev, struct bio *bio)
{ {
spin_lock_irq(&mddev->write_lock); spin_lock_irq(&mddev->lock);
wait_event_lock_irq(mddev->sb_wait, wait_event_lock_irq(mddev->sb_wait,
!mddev->flush_bio, !mddev->flush_bio,
mddev->write_lock); mddev->lock);
mddev->flush_bio = bio; mddev->flush_bio = bio;
spin_unlock_irq(&mddev->write_lock); spin_unlock_irq(&mddev->lock);
INIT_WORK(&mddev->flush_work, submit_flushes); INIT_WORK(&mddev->flush_work, submit_flushes);
queue_work(md_wq, &mddev->flush_work); queue_work(md_wq, &mddev->flush_work);
...@@ -465,7 +503,7 @@ void mddev_init(struct mddev *mddev) ...@@ -465,7 +503,7 @@ void mddev_init(struct mddev *mddev)
atomic_set(&mddev->active, 1); atomic_set(&mddev->active, 1);
atomic_set(&mddev->openers, 0); atomic_set(&mddev->openers, 0);
atomic_set(&mddev->active_io, 0); atomic_set(&mddev->active_io, 0);
spin_lock_init(&mddev->write_lock); spin_lock_init(&mddev->lock);
atomic_set(&mddev->flush_pending, 0); atomic_set(&mddev->flush_pending, 0);
init_waitqueue_head(&mddev->sb_wait); init_waitqueue_head(&mddev->sb_wait);
init_waitqueue_head(&mddev->recovery_wait); init_waitqueue_head(&mddev->recovery_wait);
...@@ -552,32 +590,9 @@ static struct mddev *mddev_find(dev_t unit) ...@@ -552,32 +590,9 @@ static struct mddev *mddev_find(dev_t unit)
goto retry; goto retry;
} }
static inline int __must_check mddev_lock(struct mddev *mddev)
{
return mutex_lock_interruptible(&mddev->reconfig_mutex);
}
/* Sometimes we need to take the lock in a situation where
* failure due to interrupts is not acceptable.
*/
static inline void mddev_lock_nointr(struct mddev *mddev)
{
mutex_lock(&mddev->reconfig_mutex);
}
static inline int mddev_is_locked(struct mddev *mddev)
{
return mutex_is_locked(&mddev->reconfig_mutex);
}
static inline int mddev_trylock(struct mddev *mddev)
{
return mutex_trylock(&mddev->reconfig_mutex);
}
static struct attribute_group md_redundancy_group; static struct attribute_group md_redundancy_group;
static void mddev_unlock(struct mddev *mddev) void mddev_unlock(struct mddev *mddev)
{ {
if (mddev->to_remove) { if (mddev->to_remove) {
/* These cannot be removed under reconfig_mutex as /* These cannot be removed under reconfig_mutex as
...@@ -619,6 +634,7 @@ static void mddev_unlock(struct mddev *mddev) ...@@ -619,6 +634,7 @@ static void mddev_unlock(struct mddev *mddev)
md_wakeup_thread(mddev->thread); md_wakeup_thread(mddev->thread);
spin_unlock(&pers_lock); spin_unlock(&pers_lock);
} }
EXPORT_SYMBOL_GPL(mddev_unlock);
static struct md_rdev *find_rdev_nr_rcu(struct mddev *mddev, int nr) static struct md_rdev *find_rdev_nr_rcu(struct mddev *mddev, int nr)
{ {
...@@ -2230,7 +2246,7 @@ static void md_update_sb(struct mddev *mddev, int force_change) ...@@ -2230,7 +2246,7 @@ static void md_update_sb(struct mddev *mddev, int force_change)
return; return;
} }
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
mddev->utime = get_seconds(); mddev->utime = get_seconds();
...@@ -2287,7 +2303,7 @@ static void md_update_sb(struct mddev *mddev, int force_change) ...@@ -2287,7 +2303,7 @@ static void md_update_sb(struct mddev *mddev, int force_change)
} }
sync_sbs(mddev, nospares); sync_sbs(mddev, nospares);
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
pr_debug("md: updating %s RAID superblock on device (in sync %d)\n", pr_debug("md: updating %s RAID superblock on device (in sync %d)\n",
mdname(mddev), mddev->in_sync); mdname(mddev), mddev->in_sync);
...@@ -2326,15 +2342,15 @@ static void md_update_sb(struct mddev *mddev, int force_change) ...@@ -2326,15 +2342,15 @@ static void md_update_sb(struct mddev *mddev, int force_change)
md_super_wait(mddev); md_super_wait(mddev);
/* if there was a failure, MD_CHANGE_DEVS was set, and we re-write super */ /* if there was a failure, MD_CHANGE_DEVS was set, and we re-write super */
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
if (mddev->in_sync != sync_req || if (mddev->in_sync != sync_req ||
test_bit(MD_CHANGE_DEVS, &mddev->flags)) { test_bit(MD_CHANGE_DEVS, &mddev->flags)) {
/* have to write it out again */ /* have to write it out again */
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
goto repeat; goto repeat;
} }
clear_bit(MD_CHANGE_PENDING, &mddev->flags); clear_bit(MD_CHANGE_PENDING, &mddev->flags);
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
wake_up(&mddev->sb_wait); wake_up(&mddev->sb_wait);
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
sysfs_notify(&mddev->kobj, NULL, "sync_completed"); sysfs_notify(&mddev->kobj, NULL, "sync_completed");
...@@ -2381,40 +2397,41 @@ state_show(struct md_rdev *rdev, char *page) ...@@ -2381,40 +2397,41 @@ state_show(struct md_rdev *rdev, char *page)
{ {
char *sep = ""; char *sep = "";
size_t len = 0; size_t len = 0;
unsigned long flags = ACCESS_ONCE(rdev->flags);
if (test_bit(Faulty, &rdev->flags) || if (test_bit(Faulty, &flags) ||
rdev->badblocks.unacked_exist) { rdev->badblocks.unacked_exist) {
len+= sprintf(page+len, "%sfaulty",sep); len+= sprintf(page+len, "%sfaulty",sep);
sep = ","; sep = ",";
} }
if (test_bit(In_sync, &rdev->flags)) { if (test_bit(In_sync, &flags)) {
len += sprintf(page+len, "%sin_sync",sep); len += sprintf(page+len, "%sin_sync",sep);
sep = ","; sep = ",";
} }
if (test_bit(WriteMostly, &rdev->flags)) { if (test_bit(WriteMostly, &flags)) {
len += sprintf(page+len, "%swrite_mostly",sep); len += sprintf(page+len, "%swrite_mostly",sep);
sep = ","; sep = ",";
} }
if (test_bit(Blocked, &rdev->flags) || if (test_bit(Blocked, &flags) ||
(rdev->badblocks.unacked_exist (rdev->badblocks.unacked_exist
&& !test_bit(Faulty, &rdev->flags))) { && !test_bit(Faulty, &flags))) {
len += sprintf(page+len, "%sblocked", sep); len += sprintf(page+len, "%sblocked", sep);
sep = ","; sep = ",";
} }
if (!test_bit(Faulty, &rdev->flags) && if (!test_bit(Faulty, &flags) &&
!test_bit(In_sync, &rdev->flags)) { !test_bit(In_sync, &flags)) {
len += sprintf(page+len, "%sspare", sep); len += sprintf(page+len, "%sspare", sep);
sep = ","; sep = ",";
} }
if (test_bit(WriteErrorSeen, &rdev->flags)) { if (test_bit(WriteErrorSeen, &flags)) {
len += sprintf(page+len, "%swrite_error", sep); len += sprintf(page+len, "%swrite_error", sep);
sep = ","; sep = ",";
} }
if (test_bit(WantReplacement, &rdev->flags)) { if (test_bit(WantReplacement, &flags)) {
len += sprintf(page+len, "%swant_replacement", sep); len += sprintf(page+len, "%swant_replacement", sep);
sep = ","; sep = ",";
} }
if (test_bit(Replacement, &rdev->flags)) { if (test_bit(Replacement, &flags)) {
len += sprintf(page+len, "%sreplacement", sep); len += sprintf(page+len, "%sreplacement", sep);
sep = ","; sep = ",";
} }
...@@ -2927,21 +2944,12 @@ rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page) ...@@ -2927,21 +2944,12 @@ rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{ {
struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr); struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr);
struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj); struct md_rdev *rdev = container_of(kobj, struct md_rdev, kobj);
struct mddev *mddev = rdev->mddev;
ssize_t rv;
if (!entry->show) if (!entry->show)
return -EIO; return -EIO;
if (!rdev->mddev)
rv = mddev ? mddev_lock(mddev) : -EBUSY; return -EBUSY;
if (!rv) { return entry->show(rdev, page);
if (rdev->mddev == NULL)
rv = -EBUSY;
else
rv = entry->show(rdev, page);
mddev_unlock(mddev);
}
return rv;
} }
static ssize_t static ssize_t
...@@ -3212,11 +3220,13 @@ safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len) ...@@ -3212,11 +3220,13 @@ safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len)
mddev->safemode_delay = 0; mddev->safemode_delay = 0;
else { else {
unsigned long old_delay = mddev->safemode_delay; unsigned long old_delay = mddev->safemode_delay;
mddev->safemode_delay = (msec*HZ)/1000; unsigned long new_delay = (msec*HZ)/1000;
if (mddev->safemode_delay == 0)
mddev->safemode_delay = 1; if (new_delay == 0)
if (mddev->safemode_delay < old_delay || old_delay == 0) new_delay = 1;
md_safemode_timeout((unsigned long)mddev); mddev->safemode_delay = new_delay;
if (new_delay < old_delay || old_delay == 0)
mod_timer(&mddev->safemode_timer, jiffies+1);
} }
return len; return len;
} }
...@@ -3226,41 +3236,52 @@ __ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store); ...@@ -3226,41 +3236,52 @@ __ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store);
static ssize_t static ssize_t
level_show(struct mddev *mddev, char *page) level_show(struct mddev *mddev, char *page)
{ {
struct md_personality *p = mddev->pers; struct md_personality *p;
int ret;
spin_lock(&mddev->lock);
p = mddev->pers;
if (p) if (p)
return sprintf(page, "%s\n", p->name); ret = sprintf(page, "%s\n", p->name);
else if (mddev->clevel[0]) else if (mddev->clevel[0])
return sprintf(page, "%s\n", mddev->clevel); ret = sprintf(page, "%s\n", mddev->clevel);
else if (mddev->level != LEVEL_NONE) else if (mddev->level != LEVEL_NONE)
return sprintf(page, "%d\n", mddev->level); ret = sprintf(page, "%d\n", mddev->level);
else else
return 0; ret = 0;
spin_unlock(&mddev->lock);
return ret;
} }
static ssize_t static ssize_t
level_store(struct mddev *mddev, const char *buf, size_t len) level_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char clevel[16]; char clevel[16];
ssize_t rv = len; ssize_t rv;
struct md_personality *pers; size_t slen = len;
struct md_personality *pers, *oldpers;
long level; long level;
void *priv; void *priv, *oldpriv;
struct md_rdev *rdev; struct md_rdev *rdev;
if (slen == 0 || slen >= sizeof(clevel))
return -EINVAL;
rv = mddev_lock(mddev);
if (rv)
return rv;
if (mddev->pers == NULL) { if (mddev->pers == NULL) {
if (len == 0) strncpy(mddev->clevel, buf, slen);
return 0; if (mddev->clevel[slen-1] == '\n')
if (len >= sizeof(mddev->clevel)) slen--;
return -ENOSPC; mddev->clevel[slen] = 0;
strncpy(mddev->clevel, buf, len);
if (mddev->clevel[len-1] == '\n')
len--;
mddev->clevel[len] = 0;
mddev->level = LEVEL_NONE; mddev->level = LEVEL_NONE;
return rv; rv = len;
goto out_unlock;
} }
rv = -EROFS;
if (mddev->ro) if (mddev->ro)
return -EROFS; goto out_unlock;
/* request to change the personality. Need to ensure: /* request to change the personality. Need to ensure:
* - array is not engaged in resync/recovery/reshape * - array is not engaged in resync/recovery/reshape
...@@ -3268,25 +3289,25 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3268,25 +3289,25 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
* - new personality will access other array. * - new personality will access other array.
*/ */
rv = -EBUSY;
if (mddev->sync_thread || if (mddev->sync_thread ||
test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
mddev->reshape_position != MaxSector || mddev->reshape_position != MaxSector ||
mddev->sysfs_active) mddev->sysfs_active)
return -EBUSY; goto out_unlock;
rv = -EINVAL;
if (!mddev->pers->quiesce) { if (!mddev->pers->quiesce) {
printk(KERN_WARNING "md: %s: %s does not support online personality change\n", printk(KERN_WARNING "md: %s: %s does not support online personality change\n",
mdname(mddev), mddev->pers->name); mdname(mddev), mddev->pers->name);
return -EINVAL; goto out_unlock;
} }
/* Now find the new personality */ /* Now find the new personality */
if (len == 0 || len >= sizeof(clevel)) strncpy(clevel, buf, slen);
return -EINVAL; if (clevel[slen-1] == '\n')
strncpy(clevel, buf, len); slen--;
if (clevel[len-1] == '\n') clevel[slen] = 0;
len--;
clevel[len] = 0;
if (kstrtol(clevel, 10, &level)) if (kstrtol(clevel, 10, &level))
level = LEVEL_NONE; level = LEVEL_NONE;
...@@ -3297,20 +3318,23 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3297,20 +3318,23 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
if (!pers || !try_module_get(pers->owner)) { if (!pers || !try_module_get(pers->owner)) {
spin_unlock(&pers_lock); spin_unlock(&pers_lock);
printk(KERN_WARNING "md: personality %s not loaded\n", clevel); printk(KERN_WARNING "md: personality %s not loaded\n", clevel);
return -EINVAL; rv = -EINVAL;
goto out_unlock;
} }
spin_unlock(&pers_lock); spin_unlock(&pers_lock);
if (pers == mddev->pers) { if (pers == mddev->pers) {
/* Nothing to do! */ /* Nothing to do! */
module_put(pers->owner); module_put(pers->owner);
return rv; rv = len;
goto out_unlock;
} }
if (!pers->takeover) { if (!pers->takeover) {
module_put(pers->owner); module_put(pers->owner);
printk(KERN_WARNING "md: %s: %s does not support personality takeover\n", printk(KERN_WARNING "md: %s: %s does not support personality takeover\n",
mdname(mddev), clevel); mdname(mddev), clevel);
return -EINVAL; rv = -EINVAL;
goto out_unlock;
} }
rdev_for_each(rdev, mddev) rdev_for_each(rdev, mddev)
...@@ -3330,30 +3354,29 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3330,30 +3354,29 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
module_put(pers->owner); module_put(pers->owner);
printk(KERN_WARNING "md: %s: %s would not accept array\n", printk(KERN_WARNING "md: %s: %s would not accept array\n",
mdname(mddev), clevel); mdname(mddev), clevel);
return PTR_ERR(priv); rv = PTR_ERR(priv);
goto out_unlock;
} }
/* Looks like we have a winner */ /* Looks like we have a winner */
mddev_suspend(mddev); mddev_suspend(mddev);
mddev->pers->stop(mddev); mddev_detach(mddev);
if (mddev->pers->sync_request == NULL && spin_lock(&mddev->lock);
pers->sync_request != NULL) { oldpers = mddev->pers;
/* need to add the md_redundancy_group */ oldpriv = mddev->private;
if (sysfs_create_group(&mddev->kobj, &md_redundancy_group)) mddev->pers = pers;
printk(KERN_WARNING mddev->private = priv;
"md: cannot register extra attributes for %s\n", strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
mdname(mddev)); mddev->level = mddev->new_level;
mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action"); mddev->layout = mddev->new_layout;
} mddev->chunk_sectors = mddev->new_chunk_sectors;
if (mddev->pers->sync_request != NULL && mddev->delta_disks = 0;
pers->sync_request == NULL) { mddev->reshape_backwards = 0;
/* need to remove the md_redundancy_group */ mddev->degraded = 0;
if (mddev->to_remove == NULL) spin_unlock(&mddev->lock);
mddev->to_remove = &md_redundancy_group;
}
if (mddev->pers->sync_request == NULL && if (oldpers->sync_request == NULL &&
mddev->external) { mddev->external) {
/* We are converting from a no-redundancy array /* We are converting from a no-redundancy array
* to a redundancy array and metadata is managed * to a redundancy array and metadata is managed
...@@ -3367,6 +3390,24 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3367,6 +3390,24 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
mddev->safemode = 0; mddev->safemode = 0;
} }
oldpers->free(mddev, oldpriv);
if (oldpers->sync_request == NULL &&
pers->sync_request != NULL) {
/* need to add the md_redundancy_group */
if (sysfs_create_group(&mddev->kobj, &md_redundancy_group))
printk(KERN_WARNING
"md: cannot register extra attributes for %s\n",
mdname(mddev));
mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action");
}
if (oldpers->sync_request != NULL &&
pers->sync_request == NULL) {
/* need to remove the md_redundancy_group */
if (mddev->to_remove == NULL)
mddev->to_remove = &md_redundancy_group;
}
rdev_for_each(rdev, mddev) { rdev_for_each(rdev, mddev) {
if (rdev->raid_disk < 0) if (rdev->raid_disk < 0)
continue; continue;
...@@ -3392,17 +3433,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3392,17 +3433,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
} }
} }
module_put(mddev->pers->owner); if (pers->sync_request == NULL) {
mddev->pers = pers;
mddev->private = priv;
strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel));
mddev->level = mddev->new_level;
mddev->layout = mddev->new_layout;
mddev->chunk_sectors = mddev->new_chunk_sectors;
mddev->delta_disks = 0;
mddev->reshape_backwards = 0;
mddev->degraded = 0;
if (mddev->pers->sync_request == NULL) {
/* this is now an array without redundancy, so /* this is now an array without redundancy, so
* it must always be in_sync * it must always be in_sync
*/ */
...@@ -3417,6 +3448,9 @@ level_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3417,6 +3448,9 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
md_update_sb(mddev, 1); md_update_sb(mddev, 1);
sysfs_notify(&mddev->kobj, NULL, "level"); sysfs_notify(&mddev->kobj, NULL, "level");
md_new_event(mddev); md_new_event(mddev);
rv = len;
out_unlock:
mddev_unlock(mddev);
return rv; return rv;
} }
...@@ -3439,28 +3473,32 @@ layout_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3439,28 +3473,32 @@ layout_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char *e; char *e;
unsigned long n = simple_strtoul(buf, &e, 10); unsigned long n = simple_strtoul(buf, &e, 10);
int err;
if (!*buf || (*e && *e != '\n')) if (!*buf || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers) { if (mddev->pers) {
int err;
if (mddev->pers->check_reshape == NULL) if (mddev->pers->check_reshape == NULL)
return -EBUSY; err = -EBUSY;
if (mddev->ro) else if (mddev->ro)
return -EROFS; err = -EROFS;
mddev->new_layout = n; else {
err = mddev->pers->check_reshape(mddev); mddev->new_layout = n;
if (err) { err = mddev->pers->check_reshape(mddev);
mddev->new_layout = mddev->layout; if (err)
return err; mddev->new_layout = mddev->layout;
} }
} else { } else {
mddev->new_layout = n; mddev->new_layout = n;
if (mddev->reshape_position == MaxSector) if (mddev->reshape_position == MaxSector)
mddev->layout = n; mddev->layout = n;
} }
return len; mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_layout = static struct md_sysfs_entry md_layout =
__ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store); __ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store);
...@@ -3483,32 +3521,39 @@ static ssize_t ...@@ -3483,32 +3521,39 @@ static ssize_t
raid_disks_store(struct mddev *mddev, const char *buf, size_t len) raid_disks_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char *e; char *e;
int rv = 0; int err;
unsigned long n = simple_strtoul(buf, &e, 10); unsigned long n = simple_strtoul(buf, &e, 10);
if (!*buf || (*e && *e != '\n')) if (!*buf || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers) if (mddev->pers)
rv = update_raid_disks(mddev, n); err = update_raid_disks(mddev, n);
else if (mddev->reshape_position != MaxSector) { else if (mddev->reshape_position != MaxSector) {
struct md_rdev *rdev; struct md_rdev *rdev;
int olddisks = mddev->raid_disks - mddev->delta_disks; int olddisks = mddev->raid_disks - mddev->delta_disks;
err = -EINVAL;
rdev_for_each(rdev, mddev) { rdev_for_each(rdev, mddev) {
if (olddisks < n && if (olddisks < n &&
rdev->data_offset < rdev->new_data_offset) rdev->data_offset < rdev->new_data_offset)
return -EINVAL; goto out_unlock;
if (olddisks > n && if (olddisks > n &&
rdev->data_offset > rdev->new_data_offset) rdev->data_offset > rdev->new_data_offset)
return -EINVAL; goto out_unlock;
} }
err = 0;
mddev->delta_disks = n - olddisks; mddev->delta_disks = n - olddisks;
mddev->raid_disks = n; mddev->raid_disks = n;
mddev->reshape_backwards = (mddev->delta_disks < 0); mddev->reshape_backwards = (mddev->delta_disks < 0);
} else } else
mddev->raid_disks = n; mddev->raid_disks = n;
return rv ? rv : len; out_unlock:
mddev_unlock(mddev);
return err ? err : len;
} }
static struct md_sysfs_entry md_raid_disks = static struct md_sysfs_entry md_raid_disks =
__ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store); __ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store);
...@@ -3527,30 +3572,34 @@ chunk_size_show(struct mddev *mddev, char *page) ...@@ -3527,30 +3572,34 @@ chunk_size_show(struct mddev *mddev, char *page)
static ssize_t static ssize_t
chunk_size_store(struct mddev *mddev, const char *buf, size_t len) chunk_size_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int err;
char *e; char *e;
unsigned long n = simple_strtoul(buf, &e, 10); unsigned long n = simple_strtoul(buf, &e, 10);
if (!*buf || (*e && *e != '\n')) if (!*buf || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers) { if (mddev->pers) {
int err;
if (mddev->pers->check_reshape == NULL) if (mddev->pers->check_reshape == NULL)
return -EBUSY; err = -EBUSY;
if (mddev->ro) else if (mddev->ro)
return -EROFS; err = -EROFS;
mddev->new_chunk_sectors = n >> 9; else {
err = mddev->pers->check_reshape(mddev); mddev->new_chunk_sectors = n >> 9;
if (err) { err = mddev->pers->check_reshape(mddev);
mddev->new_chunk_sectors = mddev->chunk_sectors; if (err)
return err; mddev->new_chunk_sectors = mddev->chunk_sectors;
} }
} else { } else {
mddev->new_chunk_sectors = n >> 9; mddev->new_chunk_sectors = n >> 9;
if (mddev->reshape_position == MaxSector) if (mddev->reshape_position == MaxSector)
mddev->chunk_sectors = n >> 9; mddev->chunk_sectors = n >> 9;
} }
return len; mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_chunk_size = static struct md_sysfs_entry md_chunk_size =
__ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store); __ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store);
...@@ -3566,20 +3615,27 @@ resync_start_show(struct mddev *mddev, char *page) ...@@ -3566,20 +3615,27 @@ resync_start_show(struct mddev *mddev, char *page)
static ssize_t static ssize_t
resync_start_store(struct mddev *mddev, const char *buf, size_t len) resync_start_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int err;
char *e; char *e;
unsigned long long n = simple_strtoull(buf, &e, 10); unsigned long long n = simple_strtoull(buf, &e, 10);
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers && !test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) if (mddev->pers && !test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
return -EBUSY; err = -EBUSY;
if (cmd_match(buf, "none")) else if (cmd_match(buf, "none"))
n = MaxSector; n = MaxSector;
else if (!*buf || (*e && *e != '\n')) else if (!*buf || (*e && *e != '\n'))
return -EINVAL; err = -EINVAL;
mddev->recovery_cp = n; if (!err) {
if (mddev->pers) mddev->recovery_cp = n;
set_bit(MD_CHANGE_CLEAN, &mddev->flags); if (mddev->pers)
return len; set_bit(MD_CHANGE_CLEAN, &mddev->flags);
}
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_resync_start = static struct md_sysfs_entry md_resync_start =
__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); __ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store);
...@@ -3677,8 +3733,39 @@ static int restart_array(struct mddev *mddev); ...@@ -3677,8 +3733,39 @@ static int restart_array(struct mddev *mddev);
static ssize_t static ssize_t
array_state_store(struct mddev *mddev, const char *buf, size_t len) array_state_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int err = -EINVAL; int err;
enum array_state st = match_word(buf, array_states); enum array_state st = match_word(buf, array_states);
if (mddev->pers && (st == active || st == clean) && mddev->ro != 1) {
/* don't take reconfig_mutex when toggling between
* clean and active
*/
spin_lock(&mddev->lock);
if (st == active) {
restart_array(mddev);
clear_bit(MD_CHANGE_PENDING, &mddev->flags);
wake_up(&mddev->sb_wait);
err = 0;
} else /* st == clean */ {
restart_array(mddev);
if (atomic_read(&mddev->writes_pending) == 0) {
if (mddev->in_sync == 0) {
mddev->in_sync = 1;
if (mddev->safemode == 1)
mddev->safemode = 0;
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
}
err = 0;
} else
err = -EBUSY;
}
spin_unlock(&mddev->lock);
return err;
}
err = mddev_lock(mddev);
if (err)
return err;
err = -EINVAL;
switch(st) { switch(st) {
case bad_word: case bad_word:
break; break;
...@@ -3722,7 +3809,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3722,7 +3809,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
case clean: case clean:
if (mddev->pers) { if (mddev->pers) {
restart_array(mddev); restart_array(mddev);
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
if (atomic_read(&mddev->writes_pending) == 0) { if (atomic_read(&mddev->writes_pending) == 0) {
if (mddev->in_sync == 0) { if (mddev->in_sync == 0) {
mddev->in_sync = 1; mddev->in_sync = 1;
...@@ -3733,7 +3820,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3733,7 +3820,7 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
err = 0; err = 0;
} else } else
err = -EBUSY; err = -EBUSY;
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
} else } else
err = -EINVAL; err = -EINVAL;
break; break;
...@@ -3754,14 +3841,14 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3754,14 +3841,14 @@ array_state_store(struct mddev *mddev, const char *buf, size_t len)
/* these cannot be set */ /* these cannot be set */
break; break;
} }
if (err)
return err; if (!err) {
else {
if (mddev->hold_active == UNTIL_IOCTL) if (mddev->hold_active == UNTIL_IOCTL)
mddev->hold_active = 0; mddev->hold_active = 0;
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
return len;
} }
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_array_state = static struct md_sysfs_entry md_array_state =
__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); __ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store);
...@@ -3822,6 +3909,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3822,6 +3909,11 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
minor != MINOR(dev)) minor != MINOR(dev))
return -EOVERFLOW; return -EOVERFLOW;
flush_workqueue(md_misc_wq);
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->persistent) { if (mddev->persistent) {
rdev = md_import_device(dev, mddev->major_version, rdev = md_import_device(dev, mddev->major_version,
mddev->minor_version); mddev->minor_version);
...@@ -3845,6 +3937,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3845,6 +3937,7 @@ new_dev_store(struct mddev *mddev, const char *buf, size_t len)
out: out:
if (err) if (err)
export_rdev(rdev); export_rdev(rdev);
mddev_unlock(mddev);
return err ? err : len; return err ? err : len;
} }
...@@ -3856,7 +3949,11 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3856,7 +3949,11 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char *end; char *end;
unsigned long chunk, end_chunk; unsigned long chunk, end_chunk;
int err;
err = mddev_lock(mddev);
if (err)
return err;
if (!mddev->bitmap) if (!mddev->bitmap)
goto out; goto out;
/* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */ /* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */
...@@ -3874,6 +3971,7 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3874,6 +3971,7 @@ bitmap_store(struct mddev *mddev, const char *buf, size_t len)
} }
bitmap_unplug(mddev->bitmap); /* flush the bits to disk */ bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
out: out:
mddev_unlock(mddev);
return len; return len;
} }
...@@ -3901,6 +3999,9 @@ size_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3901,6 +3999,9 @@ size_store(struct mddev *mddev, const char *buf, size_t len)
if (err < 0) if (err < 0)
return err; return err;
err = mddev_lock(mddev);
if (err)
return err;
if (mddev->pers) { if (mddev->pers) {
err = update_size(mddev, sectors); err = update_size(mddev, sectors);
md_update_sb(mddev, 1); md_update_sb(mddev, 1);
...@@ -3911,6 +4012,7 @@ size_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3911,6 +4012,7 @@ size_store(struct mddev *mddev, const char *buf, size_t len)
else else
err = -ENOSPC; err = -ENOSPC;
} }
mddev_unlock(mddev);
return err ? err : len; return err ? err : len;
} }
...@@ -3940,21 +4042,28 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3940,21 +4042,28 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int major, minor; int major, minor;
char *e; char *e;
int err;
/* Changing the details of 'external' metadata is /* Changing the details of 'external' metadata is
* always permitted. Otherwise there must be * always permitted. Otherwise there must be
* no devices attached to the array. * no devices attached to the array.
*/ */
err = mddev_lock(mddev);
if (err)
return err;
err = -EBUSY;
if (mddev->external && strncmp(buf, "external:", 9) == 0) if (mddev->external && strncmp(buf, "external:", 9) == 0)
; ;
else if (!list_empty(&mddev->disks)) else if (!list_empty(&mddev->disks))
return -EBUSY; goto out_unlock;
err = 0;
if (cmd_match(buf, "none")) { if (cmd_match(buf, "none")) {
mddev->persistent = 0; mddev->persistent = 0;
mddev->external = 0; mddev->external = 0;
mddev->major_version = 0; mddev->major_version = 0;
mddev->minor_version = 90; mddev->minor_version = 90;
return len; goto out_unlock;
} }
if (strncmp(buf, "external:", 9) == 0) { if (strncmp(buf, "external:", 9) == 0) {
size_t namelen = len-9; size_t namelen = len-9;
...@@ -3968,22 +4077,27 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -3968,22 +4077,27 @@ metadata_store(struct mddev *mddev, const char *buf, size_t len)
mddev->external = 1; mddev->external = 1;
mddev->major_version = 0; mddev->major_version = 0;
mddev->minor_version = 90; mddev->minor_version = 90;
return len; goto out_unlock;
} }
major = simple_strtoul(buf, &e, 10); major = simple_strtoul(buf, &e, 10);
err = -EINVAL;
if (e==buf || *e != '.') if (e==buf || *e != '.')
return -EINVAL; goto out_unlock;
buf = e+1; buf = e+1;
minor = simple_strtoul(buf, &e, 10); minor = simple_strtoul(buf, &e, 10);
if (e==buf || (*e && *e != '\n') ) if (e==buf || (*e && *e != '\n') )
return -EINVAL; goto out_unlock;
err = -ENOENT;
if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL) if (major >= ARRAY_SIZE(super_types) || super_types[major].name == NULL)
return -ENOENT; goto out_unlock;
mddev->major_version = major; mddev->major_version = major;
mddev->minor_version = minor; mddev->minor_version = minor;
mddev->persistent = 1; mddev->persistent = 1;
mddev->external = 0; mddev->external = 0;
return len; err = 0;
out_unlock:
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_metadata = static struct md_sysfs_entry md_metadata =
...@@ -3993,20 +4107,21 @@ static ssize_t ...@@ -3993,20 +4107,21 @@ static ssize_t
action_show(struct mddev *mddev, char *page) action_show(struct mddev *mddev, char *page)
{ {
char *type = "idle"; char *type = "idle";
if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) unsigned long recovery = mddev->recovery;
if (test_bit(MD_RECOVERY_FROZEN, &recovery))
type = "frozen"; type = "frozen";
else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || else if (test_bit(MD_RECOVERY_RUNNING, &recovery) ||
(!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))) { (!mddev->ro && test_bit(MD_RECOVERY_NEEDED, &recovery))) {
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) if (test_bit(MD_RECOVERY_RESHAPE, &recovery))
type = "reshape"; type = "reshape";
else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { else if (test_bit(MD_RECOVERY_SYNC, &recovery)) {
if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) if (!test_bit(MD_RECOVERY_REQUESTED, &recovery))
type = "resync"; type = "resync";
else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) else if (test_bit(MD_RECOVERY_CHECK, &recovery))
type = "check"; type = "check";
else else
type = "repair"; type = "repair";
} else if (test_bit(MD_RECOVERY_RECOVER, &mddev->recovery)) } else if (test_bit(MD_RECOVERY_RECOVER, &recovery))
type = "recover"; type = "recover";
} }
return sprintf(page, "%s\n", type); return sprintf(page, "%s\n", type);
...@@ -4027,7 +4142,10 @@ action_store(struct mddev *mddev, const char *page, size_t len) ...@@ -4027,7 +4142,10 @@ action_store(struct mddev *mddev, const char *page, size_t len)
flush_workqueue(md_misc_wq); flush_workqueue(md_misc_wq);
if (mddev->sync_thread) { if (mddev->sync_thread) {
set_bit(MD_RECOVERY_INTR, &mddev->recovery); set_bit(MD_RECOVERY_INTR, &mddev->recovery);
md_reap_sync_thread(mddev); if (mddev_lock(mddev) == 0) {
md_reap_sync_thread(mddev);
mddev_unlock(mddev);
}
} }
} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
...@@ -4041,7 +4159,11 @@ action_store(struct mddev *mddev, const char *page, size_t len) ...@@ -4041,7 +4159,11 @@ action_store(struct mddev *mddev, const char *page, size_t len)
int err; int err;
if (mddev->pers->start_reshape == NULL) if (mddev->pers->start_reshape == NULL)
return -EINVAL; return -EINVAL;
err = mddev->pers->start_reshape(mddev); err = mddev_lock(mddev);
if (!err) {
err = mddev->pers->start_reshape(mddev);
mddev_unlock(mddev);
}
if (err) if (err)
return err; return err;
sysfs_notify(&mddev->kobj, NULL, "degraded"); sysfs_notify(&mddev->kobj, NULL, "degraded");
...@@ -4225,22 +4347,36 @@ static ssize_t ...@@ -4225,22 +4347,36 @@ static ssize_t
min_sync_store(struct mddev *mddev, const char *buf, size_t len) min_sync_store(struct mddev *mddev, const char *buf, size_t len)
{ {
unsigned long long min; unsigned long long min;
int err;
int chunk;
if (kstrtoull(buf, 10, &min)) if (kstrtoull(buf, 10, &min))
return -EINVAL; return -EINVAL;
spin_lock(&mddev->lock);
err = -EINVAL;
if (min > mddev->resync_max) if (min > mddev->resync_max)
return -EINVAL; goto out_unlock;
err = -EBUSY;
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY; goto out_unlock;
/* Must be a multiple of chunk_size */ /* Must be a multiple of chunk_size */
if (mddev->chunk_sectors) { chunk = mddev->chunk_sectors;
if (chunk) {
sector_t temp = min; sector_t temp = min;
if (sector_div(temp, mddev->chunk_sectors))
return -EINVAL; err = -EINVAL;
if (sector_div(temp, chunk))
goto out_unlock;
} }
mddev->resync_min = min; mddev->resync_min = min;
err = 0;
return len; out_unlock:
spin_unlock(&mddev->lock);
return err ?: len;
} }
static struct md_sysfs_entry md_min_sync = static struct md_sysfs_entry md_min_sync =
...@@ -4258,29 +4394,42 @@ max_sync_show(struct mddev *mddev, char *page) ...@@ -4258,29 +4394,42 @@ max_sync_show(struct mddev *mddev, char *page)
static ssize_t static ssize_t
max_sync_store(struct mddev *mddev, const char *buf, size_t len) max_sync_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int err;
spin_lock(&mddev->lock);
if (strncmp(buf, "max", 3) == 0) if (strncmp(buf, "max", 3) == 0)
mddev->resync_max = MaxSector; mddev->resync_max = MaxSector;
else { else {
unsigned long long max; unsigned long long max;
int chunk;
err = -EINVAL;
if (kstrtoull(buf, 10, &max)) if (kstrtoull(buf, 10, &max))
return -EINVAL; goto out_unlock;
if (max < mddev->resync_min) if (max < mddev->resync_min)
return -EINVAL; goto out_unlock;
err = -EBUSY;
if (max < mddev->resync_max && if (max < mddev->resync_max &&
mddev->ro == 0 && mddev->ro == 0 &&
test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY; goto out_unlock;
/* Must be a multiple of chunk_size */ /* Must be a multiple of chunk_size */
if (mddev->chunk_sectors) { chunk = mddev->chunk_sectors;
if (chunk) {
sector_t temp = max; sector_t temp = max;
if (sector_div(temp, mddev->chunk_sectors))
return -EINVAL; err = -EINVAL;
if (sector_div(temp, chunk))
goto out_unlock;
} }
mddev->resync_max = max; mddev->resync_max = max;
} }
wake_up(&mddev->recovery_wait); wake_up(&mddev->recovery_wait);
return len; err = 0;
out_unlock:
spin_unlock(&mddev->lock);
return err ?: len;
} }
static struct md_sysfs_entry md_max_sync = static struct md_sysfs_entry md_max_sync =
...@@ -4297,14 +4446,20 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4297,14 +4446,20 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char *e; char *e;
unsigned long long new = simple_strtoull(buf, &e, 10); unsigned long long new = simple_strtoull(buf, &e, 10);
unsigned long long old = mddev->suspend_lo; unsigned long long old;
int err;
if (mddev->pers == NULL ||
mddev->pers->quiesce == NULL)
return -EINVAL;
if (buf == e || (*e && *e != '\n')) if (buf == e || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
err = -EINVAL;
if (mddev->pers == NULL ||
mddev->pers->quiesce == NULL)
goto unlock;
old = mddev->suspend_lo;
mddev->suspend_lo = new; mddev->suspend_lo = new;
if (new >= old) if (new >= old)
/* Shrinking suspended region */ /* Shrinking suspended region */
...@@ -4314,7 +4469,10 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4314,7 +4469,10 @@ suspend_lo_store(struct mddev *mddev, const char *buf, size_t len)
mddev->pers->quiesce(mddev, 1); mddev->pers->quiesce(mddev, 1);
mddev->pers->quiesce(mddev, 0); mddev->pers->quiesce(mddev, 0);
} }
return len; err = 0;
unlock:
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_suspend_lo = static struct md_sysfs_entry md_suspend_lo =
__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); __ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
...@@ -4330,14 +4488,20 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4330,14 +4488,20 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len)
{ {
char *e; char *e;
unsigned long long new = simple_strtoull(buf, &e, 10); unsigned long long new = simple_strtoull(buf, &e, 10);
unsigned long long old = mddev->suspend_hi; unsigned long long old;
int err;
if (mddev->pers == NULL ||
mddev->pers->quiesce == NULL)
return -EINVAL;
if (buf == e || (*e && *e != '\n')) if (buf == e || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
err = -EINVAL;
if (mddev->pers == NULL ||
mddev->pers->quiesce == NULL)
goto unlock;
old = mddev->suspend_hi;
mddev->suspend_hi = new; mddev->suspend_hi = new;
if (new <= old) if (new <= old)
/* Shrinking suspended region */ /* Shrinking suspended region */
...@@ -4347,7 +4511,10 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4347,7 +4511,10 @@ suspend_hi_store(struct mddev *mddev, const char *buf, size_t len)
mddev->pers->quiesce(mddev, 1); mddev->pers->quiesce(mddev, 1);
mddev->pers->quiesce(mddev, 0); mddev->pers->quiesce(mddev, 0);
} }
return len; err = 0;
unlock:
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_suspend_hi = static struct md_sysfs_entry md_suspend_hi =
__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
...@@ -4367,11 +4534,17 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4367,11 +4534,17 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
{ {
struct md_rdev *rdev; struct md_rdev *rdev;
char *e; char *e;
int err;
unsigned long long new = simple_strtoull(buf, &e, 10); unsigned long long new = simple_strtoull(buf, &e, 10);
if (mddev->pers)
return -EBUSY;
if (buf == e || (*e && *e != '\n')) if (buf == e || (*e && *e != '\n'))
return -EINVAL; return -EINVAL;
err = mddev_lock(mddev);
if (err)
return err;
err = -EBUSY;
if (mddev->pers)
goto unlock;
mddev->reshape_position = new; mddev->reshape_position = new;
mddev->delta_disks = 0; mddev->delta_disks = 0;
mddev->reshape_backwards = 0; mddev->reshape_backwards = 0;
...@@ -4380,7 +4553,10 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4380,7 +4553,10 @@ reshape_position_store(struct mddev *mddev, const char *buf, size_t len)
mddev->new_chunk_sectors = mddev->chunk_sectors; mddev->new_chunk_sectors = mddev->chunk_sectors;
rdev_for_each(rdev, mddev) rdev_for_each(rdev, mddev)
rdev->new_data_offset = rdev->data_offset; rdev->new_data_offset = rdev->data_offset;
return len; err = 0;
unlock:
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_reshape_position = static struct md_sysfs_entry md_reshape_position =
...@@ -4398,6 +4574,8 @@ static ssize_t ...@@ -4398,6 +4574,8 @@ static ssize_t
reshape_direction_store(struct mddev *mddev, const char *buf, size_t len) reshape_direction_store(struct mddev *mddev, const char *buf, size_t len)
{ {
int backwards = 0; int backwards = 0;
int err;
if (cmd_match(buf, "forwards")) if (cmd_match(buf, "forwards"))
backwards = 0; backwards = 0;
else if (cmd_match(buf, "backwards")) else if (cmd_match(buf, "backwards"))
...@@ -4407,16 +4585,19 @@ reshape_direction_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4407,16 +4585,19 @@ reshape_direction_store(struct mddev *mddev, const char *buf, size_t len)
if (mddev->reshape_backwards == backwards) if (mddev->reshape_backwards == backwards)
return len; return len;
err = mddev_lock(mddev);
if (err)
return err;
/* check if we are allowed to change */ /* check if we are allowed to change */
if (mddev->delta_disks) if (mddev->delta_disks)
return -EBUSY; err = -EBUSY;
else if (mddev->persistent &&
if (mddev->persistent &&
mddev->major_version == 0) mddev->major_version == 0)
return -EINVAL; err = -EINVAL;
else
mddev->reshape_backwards = backwards; mddev->reshape_backwards = backwards;
return len; mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_reshape_direction = static struct md_sysfs_entry md_reshape_direction =
...@@ -4437,6 +4618,11 @@ static ssize_t ...@@ -4437,6 +4618,11 @@ static ssize_t
array_size_store(struct mddev *mddev, const char *buf, size_t len) array_size_store(struct mddev *mddev, const char *buf, size_t len)
{ {
sector_t sectors; sector_t sectors;
int err;
err = mddev_lock(mddev);
if (err)
return err;
if (strncmp(buf, "default", 7) == 0) { if (strncmp(buf, "default", 7) == 0) {
if (mddev->pers) if (mddev->pers)
...@@ -4447,19 +4633,22 @@ array_size_store(struct mddev *mddev, const char *buf, size_t len) ...@@ -4447,19 +4633,22 @@ array_size_store(struct mddev *mddev, const char *buf, size_t len)
mddev->external_size = 0; mddev->external_size = 0;
} else { } else {
if (strict_blocks_to_sectors(buf, &sectors) < 0) if (strict_blocks_to_sectors(buf, &sectors) < 0)
return -EINVAL; err = -EINVAL;
if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors) else if (mddev->pers && mddev->pers->size(mddev, 0, 0) < sectors)
return -E2BIG; err = -E2BIG;
else
mddev->external_size = 1; mddev->external_size = 1;
} }
mddev->array_sectors = sectors; if (!err) {
if (mddev->pers) { mddev->array_sectors = sectors;
set_capacity(mddev->gendisk, mddev->array_sectors); if (mddev->pers) {
revalidate_disk(mddev->gendisk); set_capacity(mddev->gendisk, mddev->array_sectors);
revalidate_disk(mddev->gendisk);
}
} }
return len; mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry md_array_size = static struct md_sysfs_entry md_array_size =
...@@ -4523,11 +4712,7 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page) ...@@ -4523,11 +4712,7 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
mddev_get(mddev); mddev_get(mddev);
spin_unlock(&all_mddevs_lock); spin_unlock(&all_mddevs_lock);
rv = mddev_lock(mddev); rv = entry->show(mddev, page);
if (!rv) {
rv = entry->show(mddev, page);
mddev_unlock(mddev);
}
mddev_put(mddev); mddev_put(mddev);
return rv; return rv;
} }
...@@ -4551,13 +4736,7 @@ md_attr_store(struct kobject *kobj, struct attribute *attr, ...@@ -4551,13 +4736,7 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
} }
mddev_get(mddev); mddev_get(mddev);
spin_unlock(&all_mddevs_lock); spin_unlock(&all_mddevs_lock);
if (entry->store == new_dev_store) rv = entry->store(mddev, page, length);
flush_workqueue(md_misc_wq);
rv = mddev_lock(mddev);
if (!rv) {
rv = entry->store(mddev, page, length);
mddev_unlock(mddev);
}
mddev_put(mddev); mddev_put(mddev);
return rv; return rv;
} }
...@@ -4825,7 +5004,6 @@ int md_run(struct mddev *mddev) ...@@ -4825,7 +5004,6 @@ int md_run(struct mddev *mddev)
mddev->clevel); mddev->clevel);
return -EINVAL; return -EINVAL;
} }
mddev->pers = pers;
spin_unlock(&pers_lock); spin_unlock(&pers_lock);
if (mddev->level != pers->level) { if (mddev->level != pers->level) {
mddev->level = pers->level; mddev->level = pers->level;
...@@ -4836,7 +5014,6 @@ int md_run(struct mddev *mddev) ...@@ -4836,7 +5014,6 @@ int md_run(struct mddev *mddev)
if (mddev->reshape_position != MaxSector && if (mddev->reshape_position != MaxSector &&
pers->start_reshape == NULL) { pers->start_reshape == NULL) {
/* This personality cannot handle reshaping... */ /* This personality cannot handle reshaping... */
mddev->pers = NULL;
module_put(pers->owner); module_put(pers->owner);
return -EINVAL; return -EINVAL;
} }
...@@ -4880,35 +5057,38 @@ int md_run(struct mddev *mddev) ...@@ -4880,35 +5057,38 @@ int md_run(struct mddev *mddev)
if (start_readonly && mddev->ro == 0) if (start_readonly && mddev->ro == 0)
mddev->ro = 2; /* read-only, but switch on first write */ mddev->ro = 2; /* read-only, but switch on first write */
err = mddev->pers->run(mddev); err = pers->run(mddev);
if (err) if (err)
printk(KERN_ERR "md: pers->run() failed ...\n"); printk(KERN_ERR "md: pers->run() failed ...\n");
else if (mddev->pers->size(mddev, 0, 0) < mddev->array_sectors) { else if (pers->size(mddev, 0, 0) < mddev->array_sectors) {
WARN_ONCE(!mddev->external_size, "%s: default size too small," WARN_ONCE(!mddev->external_size, "%s: default size too small,"
" but 'external_size' not in effect?\n", __func__); " but 'external_size' not in effect?\n", __func__);
printk(KERN_ERR printk(KERN_ERR
"md: invalid array_size %llu > default size %llu\n", "md: invalid array_size %llu > default size %llu\n",
(unsigned long long)mddev->array_sectors / 2, (unsigned long long)mddev->array_sectors / 2,
(unsigned long long)mddev->pers->size(mddev, 0, 0) / 2); (unsigned long long)pers->size(mddev, 0, 0) / 2);
err = -EINVAL; err = -EINVAL;
mddev->pers->stop(mddev);
} }
if (err == 0 && mddev->pers->sync_request && if (err == 0 && pers->sync_request &&
(mddev->bitmap_info.file || mddev->bitmap_info.offset)) { (mddev->bitmap_info.file || mddev->bitmap_info.offset)) {
err = bitmap_create(mddev); err = bitmap_create(mddev);
if (err) { if (err)
printk(KERN_ERR "%s: failed to create bitmap (%d)\n", printk(KERN_ERR "%s: failed to create bitmap (%d)\n",
mdname(mddev), err); mdname(mddev), err);
mddev->pers->stop(mddev);
}
} }
if (err) { if (err) {
module_put(mddev->pers->owner); mddev_detach(mddev);
mddev->pers = NULL; pers->free(mddev, mddev->private);
module_put(pers->owner);
bitmap_destroy(mddev); bitmap_destroy(mddev);
return err; return err;
} }
if (mddev->pers->sync_request) { if (mddev->queue) {
mddev->queue->backing_dev_info.congested_data = mddev;
mddev->queue->backing_dev_info.congested_fn = md_congested;
blk_queue_merge_bvec(mddev->queue, md_mergeable_bvec);
}
if (pers->sync_request) {
if (mddev->kobj.sd && if (mddev->kobj.sd &&
sysfs_create_group(&mddev->kobj, &md_redundancy_group)) sysfs_create_group(&mddev->kobj, &md_redundancy_group))
printk(KERN_WARNING printk(KERN_WARNING
...@@ -4927,7 +5107,10 @@ int md_run(struct mddev *mddev) ...@@ -4927,7 +5107,10 @@ int md_run(struct mddev *mddev)
mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */ mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
mddev->in_sync = 1; mddev->in_sync = 1;
smp_wmb(); smp_wmb();
spin_lock(&mddev->lock);
mddev->pers = pers;
mddev->ready = 1; mddev->ready = 1;
spin_unlock(&mddev->lock);
rdev_for_each(rdev, mddev) rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0) if (rdev->raid_disk >= 0)
if (sysfs_link_rdev(mddev, rdev)) if (sysfs_link_rdev(mddev, rdev))
...@@ -5070,14 +5253,38 @@ void md_stop_writes(struct mddev *mddev) ...@@ -5070,14 +5253,38 @@ void md_stop_writes(struct mddev *mddev)
} }
EXPORT_SYMBOL_GPL(md_stop_writes); EXPORT_SYMBOL_GPL(md_stop_writes);
static void mddev_detach(struct mddev *mddev)
{
struct bitmap *bitmap = mddev->bitmap;
/* wait for behind writes to complete */
if (bitmap && atomic_read(&bitmap->behind_writes) > 0) {
printk(KERN_INFO "md:%s: behind writes in progress - waiting to stop.\n",
mdname(mddev));
/* need to kick something here to make sure I/O goes? */
wait_event(bitmap->behind_wait,
atomic_read(&bitmap->behind_writes) == 0);
}
if (mddev->pers && mddev->pers->quiesce) {
mddev->pers->quiesce(mddev, 1);
mddev->pers->quiesce(mddev, 0);
}
md_unregister_thread(&mddev->thread);
if (mddev->queue)
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
}
static void __md_stop(struct mddev *mddev) static void __md_stop(struct mddev *mddev)
{ {
struct md_personality *pers = mddev->pers;
mddev_detach(mddev);
spin_lock(&mddev->lock);
mddev->ready = 0; mddev->ready = 0;
mddev->pers->stop(mddev);
if (mddev->pers->sync_request && mddev->to_remove == NULL)
mddev->to_remove = &md_redundancy_group;
module_put(mddev->pers->owner);
mddev->pers = NULL; mddev->pers = NULL;
spin_unlock(&mddev->lock);
pers->free(mddev, mddev->private);
if (pers->sync_request && mddev->to_remove == NULL)
mddev->to_remove = &md_redundancy_group;
module_put(pers->owner);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
} }
...@@ -5226,8 +5433,11 @@ static int do_md_stop(struct mddev *mddev, int mode, ...@@ -5226,8 +5433,11 @@ static int do_md_stop(struct mddev *mddev, int mode,
bitmap_destroy(mddev); bitmap_destroy(mddev);
if (mddev->bitmap_info.file) { if (mddev->bitmap_info.file) {
fput(mddev->bitmap_info.file); struct file *f = mddev->bitmap_info.file;
spin_lock(&mddev->lock);
mddev->bitmap_info.file = NULL; mddev->bitmap_info.file = NULL;
spin_unlock(&mddev->lock);
fput(f);
} }
mddev->bitmap_info.offset = 0; mddev->bitmap_info.offset = 0;
...@@ -5436,37 +5646,31 @@ static int get_array_info(struct mddev *mddev, void __user *arg) ...@@ -5436,37 +5646,31 @@ static int get_array_info(struct mddev *mddev, void __user *arg)
static int get_bitmap_file(struct mddev *mddev, void __user * arg) static int get_bitmap_file(struct mddev *mddev, void __user * arg)
{ {
mdu_bitmap_file_t *file = NULL; /* too big for stack allocation */ mdu_bitmap_file_t *file = NULL; /* too big for stack allocation */
char *ptr, *buf = NULL; char *ptr;
int err = -ENOMEM; int err;
file = kmalloc(sizeof(*file), GFP_NOIO); file = kmalloc(sizeof(*file), GFP_NOIO);
if (!file) if (!file)
goto out; return -ENOMEM;
err = 0;
spin_lock(&mddev->lock);
/* bitmap disabled, zero the first byte and copy out */ /* bitmap disabled, zero the first byte and copy out */
if (!mddev->bitmap || !mddev->bitmap->storage.file) { if (!mddev->bitmap_info.file)
file->pathname[0] = '\0'; file->pathname[0] = '\0';
goto copy_out; else if ((ptr = d_path(&mddev->bitmap_info.file->f_path,
} file->pathname, sizeof(file->pathname))),
IS_ERR(ptr))
buf = kmalloc(sizeof(file->pathname), GFP_KERNEL); err = PTR_ERR(ptr);
if (!buf) else
goto out; memmove(file->pathname, ptr,
sizeof(file->pathname)-(ptr-file->pathname));
ptr = d_path(&mddev->bitmap->storage.file->f_path, spin_unlock(&mddev->lock);
buf, sizeof(file->pathname));
if (IS_ERR(ptr))
goto out;
strcpy(file->pathname, ptr);
copy_out: if (err == 0 &&
err = 0; copy_to_user(arg, file, sizeof(*file)))
if (copy_to_user(arg, file, sizeof(*file)))
err = -EFAULT; err = -EFAULT;
out:
kfree(buf);
kfree(file); kfree(file);
return err; return err;
} }
...@@ -5789,22 +5993,24 @@ static int set_bitmap_file(struct mddev *mddev, int fd) ...@@ -5789,22 +5993,24 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
if (fd >= 0) { if (fd >= 0) {
struct inode *inode; struct inode *inode;
if (mddev->bitmap) struct file *f;
if (mddev->bitmap || mddev->bitmap_info.file)
return -EEXIST; /* cannot add when bitmap is present */ return -EEXIST; /* cannot add when bitmap is present */
mddev->bitmap_info.file = fget(fd); f = fget(fd);
if (mddev->bitmap_info.file == NULL) { if (f == NULL) {
printk(KERN_ERR "%s: error: failed to get bitmap file\n", printk(KERN_ERR "%s: error: failed to get bitmap file\n",
mdname(mddev)); mdname(mddev));
return -EBADF; return -EBADF;
} }
inode = mddev->bitmap_info.file->f_mapping->host; inode = f->f_mapping->host;
if (!S_ISREG(inode->i_mode)) { if (!S_ISREG(inode->i_mode)) {
printk(KERN_ERR "%s: error: bitmap file must be a regular file\n", printk(KERN_ERR "%s: error: bitmap file must be a regular file\n",
mdname(mddev)); mdname(mddev));
err = -EBADF; err = -EBADF;
} else if (!(mddev->bitmap_info.file->f_mode & FMODE_WRITE)) { } else if (!(f->f_mode & FMODE_WRITE)) {
printk(KERN_ERR "%s: error: bitmap file must open for write\n", printk(KERN_ERR "%s: error: bitmap file must open for write\n",
mdname(mddev)); mdname(mddev));
err = -EBADF; err = -EBADF;
...@@ -5814,10 +6020,10 @@ static int set_bitmap_file(struct mddev *mddev, int fd) ...@@ -5814,10 +6020,10 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
err = -EBUSY; err = -EBUSY;
} }
if (err) { if (err) {
fput(mddev->bitmap_info.file); fput(f);
mddev->bitmap_info.file = NULL;
return err; return err;
} }
mddev->bitmap_info.file = f;
mddev->bitmap_info.offset = 0; /* file overrides offset */ mddev->bitmap_info.offset = 0; /* file overrides offset */
} else if (mddev->bitmap == NULL) } else if (mddev->bitmap == NULL)
return -ENOENT; /* cannot remove what isn't there */ return -ENOENT; /* cannot remove what isn't there */
...@@ -5836,9 +6042,13 @@ static int set_bitmap_file(struct mddev *mddev, int fd) ...@@ -5836,9 +6042,13 @@ static int set_bitmap_file(struct mddev *mddev, int fd)
mddev->pers->quiesce(mddev, 0); mddev->pers->quiesce(mddev, 0);
} }
if (fd < 0) { if (fd < 0) {
if (mddev->bitmap_info.file) struct file *f = mddev->bitmap_info.file;
fput(mddev->bitmap_info.file); if (f) {
mddev->bitmap_info.file = NULL; spin_lock(&mddev->lock);
mddev->bitmap_info.file = NULL;
spin_unlock(&mddev->lock);
fput(f);
}
} }
return err; return err;
...@@ -6251,6 +6461,11 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -6251,6 +6461,11 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
case SET_DISK_FAULTY: case SET_DISK_FAULTY:
err = set_disk_faulty(mddev, new_decode_dev(arg)); err = set_disk_faulty(mddev, new_decode_dev(arg));
goto out; goto out;
case GET_BITMAP_FILE:
err = get_bitmap_file(mddev, argp);
goto out;
} }
if (cmd == ADD_NEW_DISK) if (cmd == ADD_NEW_DISK)
...@@ -6342,10 +6557,6 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -6342,10 +6557,6 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
* Commands even a read-only array can execute: * Commands even a read-only array can execute:
*/ */
switch (cmd) { switch (cmd) {
case GET_BITMAP_FILE:
err = get_bitmap_file(mddev, argp);
goto unlock;
case RESTART_ARRAY_RW: case RESTART_ARRAY_RW:
err = restart_array(mddev); err = restart_array(mddev);
goto unlock; goto unlock;
...@@ -6873,9 +7084,7 @@ static int md_seq_show(struct seq_file *seq, void *v) ...@@ -6873,9 +7084,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
return 0; return 0;
} }
if (mddev_lock(mddev) < 0) spin_lock(&mddev->lock);
return -EINTR;
if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) { if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) {
seq_printf(seq, "%s : %sactive", mdname(mddev), seq_printf(seq, "%s : %sactive", mdname(mddev),
mddev->pers ? "" : "in"); mddev->pers ? "" : "in");
...@@ -6888,7 +7097,8 @@ static int md_seq_show(struct seq_file *seq, void *v) ...@@ -6888,7 +7097,8 @@ static int md_seq_show(struct seq_file *seq, void *v)
} }
sectors = 0; sectors = 0;
rdev_for_each(rdev, mddev) { rcu_read_lock();
rdev_for_each_rcu(rdev, mddev) {
char b[BDEVNAME_SIZE]; char b[BDEVNAME_SIZE];
seq_printf(seq, " %s[%d]", seq_printf(seq, " %s[%d]",
bdevname(rdev->bdev,b), rdev->desc_nr); bdevname(rdev->bdev,b), rdev->desc_nr);
...@@ -6904,6 +7114,7 @@ static int md_seq_show(struct seq_file *seq, void *v) ...@@ -6904,6 +7114,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "(R)"); seq_printf(seq, "(R)");
sectors += rdev->sectors; sectors += rdev->sectors;
} }
rcu_read_unlock();
if (!list_empty(&mddev->disks)) { if (!list_empty(&mddev->disks)) {
if (mddev->pers) if (mddev->pers)
...@@ -6946,7 +7157,7 @@ static int md_seq_show(struct seq_file *seq, void *v) ...@@ -6946,7 +7157,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "\n"); seq_printf(seq, "\n");
} }
mddev_unlock(mddev); spin_unlock(&mddev->lock);
return 0; return 0;
} }
...@@ -7102,7 +7313,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi) ...@@ -7102,7 +7313,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi)
if (mddev->safemode == 1) if (mddev->safemode == 1)
mddev->safemode = 0; mddev->safemode = 0;
if (mddev->in_sync) { if (mddev->in_sync) {
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
if (mddev->in_sync) { if (mddev->in_sync) {
mddev->in_sync = 0; mddev->in_sync = 0;
set_bit(MD_CHANGE_CLEAN, &mddev->flags); set_bit(MD_CHANGE_CLEAN, &mddev->flags);
...@@ -7110,7 +7321,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi) ...@@ -7110,7 +7321,7 @@ void md_write_start(struct mddev *mddev, struct bio *bi)
md_wakeup_thread(mddev->thread); md_wakeup_thread(mddev->thread);
did_change = 1; did_change = 1;
} }
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
} }
if (did_change) if (did_change)
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
...@@ -7148,7 +7359,7 @@ int md_allow_write(struct mddev *mddev) ...@@ -7148,7 +7359,7 @@ int md_allow_write(struct mddev *mddev)
if (!mddev->pers->sync_request) if (!mddev->pers->sync_request)
return 0; return 0;
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
if (mddev->in_sync) { if (mddev->in_sync) {
mddev->in_sync = 0; mddev->in_sync = 0;
set_bit(MD_CHANGE_CLEAN, &mddev->flags); set_bit(MD_CHANGE_CLEAN, &mddev->flags);
...@@ -7156,11 +7367,11 @@ int md_allow_write(struct mddev *mddev) ...@@ -7156,11 +7367,11 @@ int md_allow_write(struct mddev *mddev)
if (mddev->safemode_delay && if (mddev->safemode_delay &&
mddev->safemode == 0) mddev->safemode == 0)
mddev->safemode = 1; mddev->safemode = 1;
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
md_update_sb(mddev, 0); md_update_sb(mddev, 0);
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
} else } else
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
if (test_bit(MD_CHANGE_PENDING, &mddev->flags)) if (test_bit(MD_CHANGE_PENDING, &mddev->flags))
return -EAGAIN; return -EAGAIN;
...@@ -7513,6 +7724,7 @@ void md_do_sync(struct md_thread *thread) ...@@ -7513,6 +7724,7 @@ void md_do_sync(struct md_thread *thread)
skip: skip:
set_bit(MD_CHANGE_DEVS, &mddev->flags); set_bit(MD_CHANGE_DEVS, &mddev->flags);
spin_lock(&mddev->lock);
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
/* We completed so min/max setting can be forgotten if used. */ /* We completed so min/max setting can be forgotten if used. */
if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
...@@ -7521,6 +7733,8 @@ void md_do_sync(struct md_thread *thread) ...@@ -7521,6 +7733,8 @@ void md_do_sync(struct md_thread *thread)
} else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
mddev->resync_min = mddev->curr_resync_completed; mddev->resync_min = mddev->curr_resync_completed;
mddev->curr_resync = 0; mddev->curr_resync = 0;
spin_unlock(&mddev->lock);
wake_up(&resync_wait); wake_up(&resync_wait);
set_bit(MD_RECOVERY_DONE, &mddev->recovery); set_bit(MD_RECOVERY_DONE, &mddev->recovery);
md_wakeup_thread(mddev->thread); md_wakeup_thread(mddev->thread);
...@@ -7688,7 +7902,7 @@ void md_check_recovery(struct mddev *mddev) ...@@ -7688,7 +7902,7 @@ void md_check_recovery(struct mddev *mddev)
if (!mddev->external) { if (!mddev->external) {
int did_change = 0; int did_change = 0;
spin_lock_irq(&mddev->write_lock); spin_lock(&mddev->lock);
if (mddev->safemode && if (mddev->safemode &&
!atomic_read(&mddev->writes_pending) && !atomic_read(&mddev->writes_pending) &&
!mddev->in_sync && !mddev->in_sync &&
...@@ -7699,7 +7913,7 @@ void md_check_recovery(struct mddev *mddev) ...@@ -7699,7 +7913,7 @@ void md_check_recovery(struct mddev *mddev)
} }
if (mddev->safemode == 1) if (mddev->safemode == 1)
mddev->safemode = 0; mddev->safemode = 0;
spin_unlock_irq(&mddev->write_lock); spin_unlock(&mddev->lock);
if (did_change) if (did_change)
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
} }
...@@ -7721,7 +7935,9 @@ void md_check_recovery(struct mddev *mddev) ...@@ -7721,7 +7935,9 @@ void md_check_recovery(struct mddev *mddev)
* any transients in the value of "sync_action". * any transients in the value of "sync_action".
*/ */
mddev->curr_resync_completed = 0; mddev->curr_resync_completed = 0;
spin_lock(&mddev->lock);
set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
spin_unlock(&mddev->lock);
/* Clear some bits that don't mean anything, but /* Clear some bits that don't mean anything, but
* might be left set * might be left set
*/ */
......
...@@ -386,7 +386,18 @@ struct mddev { ...@@ -386,7 +386,18 @@ struct mddev {
struct work_struct del_work; /* used for delayed sysfs removal */ struct work_struct del_work; /* used for delayed sysfs removal */
spinlock_t write_lock; /* "lock" protects:
* flush_bio transition from NULL to !NULL
* rdev superblocks, events
* clearing MD_CHANGE_*
* in_sync - and related safemode and MD_CHANGE changes
* pers (also protected by reconfig_mutex and pending IO).
* clearing ->bitmap
* clearing ->bitmap_info.file
* changing ->resync_{min,max}
* setting MD_RECOVERY_RUNNING (which interacts with resync_{min,max})
*/
spinlock_t lock;
wait_queue_head_t sb_wait; /* for waiting on superblock updates */ wait_queue_head_t sb_wait; /* for waiting on superblock updates */
atomic_t pending_writes; /* number of active superblock writes */ atomic_t pending_writes; /* number of active superblock writes */
...@@ -439,13 +450,30 @@ struct mddev { ...@@ -439,13 +450,30 @@ struct mddev {
void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev); void (*sync_super)(struct mddev *mddev, struct md_rdev *rdev);
}; };
static inline void rdev_dec_pending(struct md_rdev *rdev, struct mddev *mddev) static inline int __must_check mddev_lock(struct mddev *mddev)
{ {
int faulty = test_bit(Faulty, &rdev->flags); return mutex_lock_interruptible(&mddev->reconfig_mutex);
if (atomic_dec_and_test(&rdev->nr_pending) && faulty) }
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
/* Sometimes we need to take the lock in a situation where
* failure due to interrupts is not acceptable.
*/
static inline void mddev_lock_nointr(struct mddev *mddev)
{
mutex_lock(&mddev->reconfig_mutex);
}
static inline int mddev_is_locked(struct mddev *mddev)
{
return mutex_is_locked(&mddev->reconfig_mutex);
} }
static inline int mddev_trylock(struct mddev *mddev)
{
return mutex_trylock(&mddev->reconfig_mutex);
}
extern void mddev_unlock(struct mddev *mddev);
static inline void md_sync_acct(struct block_device *bdev, unsigned long nr_sectors) static inline void md_sync_acct(struct block_device *bdev, unsigned long nr_sectors)
{ {
atomic_add(nr_sectors, &bdev->bd_contains->bd_disk->sync_io); atomic_add(nr_sectors, &bdev->bd_contains->bd_disk->sync_io);
...@@ -459,7 +487,7 @@ struct md_personality ...@@ -459,7 +487,7 @@ struct md_personality
struct module *owner; struct module *owner;
void (*make_request)(struct mddev *mddev, struct bio *bio); void (*make_request)(struct mddev *mddev, struct bio *bio);
int (*run)(struct mddev *mddev); int (*run)(struct mddev *mddev);
int (*stop)(struct mddev *mddev); void (*free)(struct mddev *mddev, void *priv);
void (*status)(struct seq_file *seq, struct mddev *mddev); void (*status)(struct seq_file *seq, struct mddev *mddev);
/* error_handler must set ->faulty and clear ->in_sync /* error_handler must set ->faulty and clear ->in_sync
* if appropriate, and should abort recovery if needed * if appropriate, and should abort recovery if needed
...@@ -490,6 +518,13 @@ struct md_personality ...@@ -490,6 +518,13 @@ struct md_personality
* array. * array.
*/ */
void *(*takeover) (struct mddev *mddev); void *(*takeover) (struct mddev *mddev);
/* congested implements bdi.congested_fn().
* Will not be called while array is 'suspended' */
int (*congested)(struct mddev *mddev, int bits);
/* mergeable_bvec is use to implement ->merge_bvec_fn */
int (*mergeable_bvec)(struct mddev *mddev,
struct bvec_merge_data *bvm,
struct bio_vec *biovec);
}; };
struct md_sysfs_entry { struct md_sysfs_entry {
...@@ -624,4 +659,14 @@ static inline int mddev_check_plugged(struct mddev *mddev) ...@@ -624,4 +659,14 @@ static inline int mddev_check_plugged(struct mddev *mddev)
return !!blk_check_plugged(md_unplug, mddev, return !!blk_check_plugged(md_unplug, mddev,
sizeof(struct blk_plug_cb)); sizeof(struct blk_plug_cb));
} }
static inline void rdev_dec_pending(struct md_rdev *rdev, struct mddev *mddev)
{
int faulty = test_bit(Faulty, &rdev->flags);
if (atomic_dec_and_test(&rdev->nr_pending) && faulty) {
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
}
}
#endif /* _MD_MD_H */ #endif /* _MD_MD_H */
...@@ -153,15 +153,11 @@ static void multipath_status (struct seq_file *seq, struct mddev *mddev) ...@@ -153,15 +153,11 @@ static void multipath_status (struct seq_file *seq, struct mddev *mddev)
seq_printf (seq, "]"); seq_printf (seq, "]");
} }
static int multipath_congested(void *data, int bits) static int multipath_congested(struct mddev *mddev, int bits)
{ {
struct mddev *mddev = data;
struct mpconf *conf = mddev->private; struct mpconf *conf = mddev->private;
int i, ret = 0; int i, ret = 0;
if (mddev_congested(mddev, bits))
return 1;
rcu_read_lock(); rcu_read_lock();
for (i = 0; i < mddev->raid_disks ; i++) { for (i = 0; i < mddev->raid_disks ; i++) {
struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev); struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
...@@ -403,7 +399,7 @@ static int multipath_run (struct mddev *mddev) ...@@ -403,7 +399,7 @@ static int multipath_run (struct mddev *mddev)
/* /*
* copy the already verified devices into our private MULTIPATH * copy the already verified devices into our private MULTIPATH
* bookkeeping area. [whatever we allocate in multipath_run(), * bookkeeping area. [whatever we allocate in multipath_run(),
* should be freed in multipath_stop()] * should be freed in multipath_free()]
*/ */
conf = kzalloc(sizeof(struct mpconf), GFP_KERNEL); conf = kzalloc(sizeof(struct mpconf), GFP_KERNEL);
...@@ -489,9 +485,6 @@ static int multipath_run (struct mddev *mddev) ...@@ -489,9 +485,6 @@ static int multipath_run (struct mddev *mddev)
*/ */
md_set_array_sectors(mddev, multipath_size(mddev, 0, 0)); md_set_array_sectors(mddev, multipath_size(mddev, 0, 0));
mddev->queue->backing_dev_info.congested_fn = multipath_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
if (md_integrity_register(mddev)) if (md_integrity_register(mddev))
goto out_free_conf; goto out_free_conf;
...@@ -507,17 +500,13 @@ static int multipath_run (struct mddev *mddev) ...@@ -507,17 +500,13 @@ static int multipath_run (struct mddev *mddev)
return -EIO; return -EIO;
} }
static int multipath_stop (struct mddev *mddev) static void multipath_free(struct mddev *mddev, void *priv)
{ {
struct mpconf *conf = mddev->private; struct mpconf *conf = priv;
md_unregister_thread(&mddev->thread);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
mempool_destroy(conf->pool); mempool_destroy(conf->pool);
kfree(conf->multipaths); kfree(conf->multipaths);
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
static struct md_personality multipath_personality = static struct md_personality multipath_personality =
...@@ -527,12 +516,13 @@ static struct md_personality multipath_personality = ...@@ -527,12 +516,13 @@ static struct md_personality multipath_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = multipath_make_request, .make_request = multipath_make_request,
.run = multipath_run, .run = multipath_run,
.stop = multipath_stop, .free = multipath_free,
.status = multipath_status, .status = multipath_status,
.error_handler = multipath_error, .error_handler = multipath_error,
.hot_add_disk = multipath_add_disk, .hot_add_disk = multipath_add_disk,
.hot_remove_disk= multipath_remove_disk, .hot_remove_disk= multipath_remove_disk,
.size = multipath_size, .size = multipath_size,
.congested = multipath_congested,
}; };
static int __init multipath_init (void) static int __init multipath_init (void)
......
...@@ -25,17 +25,13 @@ ...@@ -25,17 +25,13 @@
#include "raid0.h" #include "raid0.h"
#include "raid5.h" #include "raid5.h"
static int raid0_congested(void *data, int bits) static int raid0_congested(struct mddev *mddev, int bits)
{ {
struct mddev *mddev = data;
struct r0conf *conf = mddev->private; struct r0conf *conf = mddev->private;
struct md_rdev **devlist = conf->devlist; struct md_rdev **devlist = conf->devlist;
int raid_disks = conf->strip_zone[0].nb_dev; int raid_disks = conf->strip_zone[0].nb_dev;
int i, ret = 0; int i, ret = 0;
if (mddev_congested(mddev, bits))
return 1;
for (i = 0; i < raid_disks && !ret ; i++) { for (i = 0; i < raid_disks && !ret ; i++) {
struct request_queue *q = bdev_get_queue(devlist[i]->bdev); struct request_queue *q = bdev_get_queue(devlist[i]->bdev);
...@@ -263,8 +259,6 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) ...@@ -263,8 +259,6 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
mdname(mddev), mdname(mddev),
(unsigned long long)smallest->sectors); (unsigned long long)smallest->sectors);
} }
mddev->queue->backing_dev_info.congested_fn = raid0_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
/* /*
* now since we have the hard sector sizes, we can make sure * now since we have the hard sector sizes, we can make sure
...@@ -356,17 +350,16 @@ static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone, ...@@ -356,17 +350,16 @@ static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone,
/** /**
* raid0_mergeable_bvec -- tell bio layer if two requests can be merged * raid0_mergeable_bvec -- tell bio layer if two requests can be merged
* @q: request queue * @mddev: the md device
* @bvm: properties of new bio * @bvm: properties of new bio
* @biovec: the request that could be merged to it. * @biovec: the request that could be merged to it.
* *
* Return amount of bytes we can accept at this offset * Return amount of bytes we can accept at this offset
*/ */
static int raid0_mergeable_bvec(struct request_queue *q, static int raid0_mergeable_bvec(struct mddev *mddev,
struct bvec_merge_data *bvm, struct bvec_merge_data *bvm,
struct bio_vec *biovec) struct bio_vec *biovec)
{ {
struct mddev *mddev = q->queuedata;
struct r0conf *conf = mddev->private; struct r0conf *conf = mddev->private;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
sector_t sector_offset = sector; sector_t sector_offset = sector;
...@@ -422,7 +415,7 @@ static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks ...@@ -422,7 +415,7 @@ static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks
return array_sectors; return array_sectors;
} }
static int raid0_stop(struct mddev *mddev); static void raid0_free(struct mddev *mddev, void *priv);
static int raid0_run(struct mddev *mddev) static int raid0_run(struct mddev *mddev)
{ {
...@@ -471,26 +464,22 @@ static int raid0_run(struct mddev *mddev) ...@@ -471,26 +464,22 @@ static int raid0_run(struct mddev *mddev)
mddev->queue->backing_dev_info.ra_pages = 2* stripe; mddev->queue->backing_dev_info.ra_pages = 2* stripe;
} }
blk_queue_merge_bvec(mddev->queue, raid0_mergeable_bvec);
dump_zones(mddev); dump_zones(mddev);
ret = md_integrity_register(mddev); ret = md_integrity_register(mddev);
if (ret) if (ret)
raid0_stop(mddev); raid0_free(mddev, conf);
return ret; return ret;
} }
static int raid0_stop(struct mddev *mddev) static void raid0_free(struct mddev *mddev, void *priv)
{ {
struct r0conf *conf = mddev->private; struct r0conf *conf = priv;
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
kfree(conf->strip_zone); kfree(conf->strip_zone);
kfree(conf->devlist); kfree(conf->devlist);
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
/* /*
...@@ -724,11 +713,13 @@ static struct md_personality raid0_personality= ...@@ -724,11 +713,13 @@ static struct md_personality raid0_personality=
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = raid0_make_request, .make_request = raid0_make_request,
.run = raid0_run, .run = raid0_run,
.stop = raid0_stop, .free = raid0_free,
.status = raid0_status, .status = raid0_status,
.size = raid0_size, .size = raid0_size,
.takeover = raid0_takeover, .takeover = raid0_takeover,
.quiesce = raid0_quiesce, .quiesce = raid0_quiesce,
.congested = raid0_congested,
.mergeable_bvec = raid0_mergeable_bvec,
}; };
static int __init raid0_init (void) static int __init raid0_init (void)
......
...@@ -701,11 +701,10 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect ...@@ -701,11 +701,10 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
return best_disk; return best_disk;
} }
static int raid1_mergeable_bvec(struct request_queue *q, static int raid1_mergeable_bvec(struct mddev *mddev,
struct bvec_merge_data *bvm, struct bvec_merge_data *bvm,
struct bio_vec *biovec) struct bio_vec *biovec)
{ {
struct mddev *mddev = q->queuedata;
struct r1conf *conf = mddev->private; struct r1conf *conf = mddev->private;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max = biovec->bv_len; int max = biovec->bv_len;
...@@ -734,7 +733,7 @@ static int raid1_mergeable_bvec(struct request_queue *q, ...@@ -734,7 +733,7 @@ static int raid1_mergeable_bvec(struct request_queue *q,
} }
int md_raid1_congested(struct mddev *mddev, int bits) static int raid1_congested(struct mddev *mddev, int bits)
{ {
struct r1conf *conf = mddev->private; struct r1conf *conf = mddev->private;
int i, ret = 0; int i, ret = 0;
...@@ -763,15 +762,6 @@ int md_raid1_congested(struct mddev *mddev, int bits) ...@@ -763,15 +762,6 @@ int md_raid1_congested(struct mddev *mddev, int bits)
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(md_raid1_congested);
static int raid1_congested(void *data, int bits)
{
struct mddev *mddev = data;
return mddev_congested(mddev, bits) ||
md_raid1_congested(mddev, bits);
}
static void flush_pending_writes(struct r1conf *conf) static void flush_pending_writes(struct r1conf *conf)
{ {
...@@ -2882,7 +2872,7 @@ static struct r1conf *setup_conf(struct mddev *mddev) ...@@ -2882,7 +2872,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
return ERR_PTR(err); return ERR_PTR(err);
} }
static int stop(struct mddev *mddev); static void raid1_free(struct mddev *mddev, void *priv);
static int run(struct mddev *mddev) static int run(struct mddev *mddev)
{ {
struct r1conf *conf; struct r1conf *conf;
...@@ -2904,7 +2894,7 @@ static int run(struct mddev *mddev) ...@@ -2904,7 +2894,7 @@ static int run(struct mddev *mddev)
/* /*
* copy the already verified devices into our private RAID1 * copy the already verified devices into our private RAID1
* bookkeeping area. [whatever we allocate in run(), * bookkeeping area. [whatever we allocate in run(),
* should be freed in stop()] * should be freed in raid1_free()]
*/ */
if (mddev->private == NULL) if (mddev->private == NULL)
conf = setup_conf(mddev); conf = setup_conf(mddev);
...@@ -2955,10 +2945,6 @@ static int run(struct mddev *mddev) ...@@ -2955,10 +2945,6 @@ static int run(struct mddev *mddev)
md_set_array_sectors(mddev, raid1_size(mddev, 0, 0)); md_set_array_sectors(mddev, raid1_size(mddev, 0, 0));
if (mddev->queue) { if (mddev->queue) {
mddev->queue->backing_dev_info.congested_fn = raid1_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
blk_queue_merge_bvec(mddev->queue, raid1_mergeable_bvec);
if (discard_supported) if (discard_supported)
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
mddev->queue); mddev->queue);
...@@ -2968,37 +2954,23 @@ static int run(struct mddev *mddev) ...@@ -2968,37 +2954,23 @@ static int run(struct mddev *mddev)
} }
ret = md_integrity_register(mddev); ret = md_integrity_register(mddev);
if (ret) if (ret) {
stop(mddev); md_unregister_thread(&mddev->thread);
raid1_free(mddev, conf);
}
return ret; return ret;
} }
static int stop(struct mddev *mddev) static void raid1_free(struct mddev *mddev, void *priv)
{ {
struct r1conf *conf = mddev->private; struct r1conf *conf = priv;
struct bitmap *bitmap = mddev->bitmap;
/* wait for behind writes to complete */
if (bitmap && atomic_read(&bitmap->behind_writes) > 0) {
printk(KERN_INFO "md/raid1:%s: behind writes in progress - waiting to stop.\n",
mdname(mddev));
/* need to kick something here to make sure I/O goes? */
wait_event(bitmap->behind_wait,
atomic_read(&bitmap->behind_writes) == 0);
}
freeze_array(conf, 0);
unfreeze_array(conf);
md_unregister_thread(&mddev->thread);
if (conf->r1bio_pool) if (conf->r1bio_pool)
mempool_destroy(conf->r1bio_pool); mempool_destroy(conf->r1bio_pool);
kfree(conf->mirrors); kfree(conf->mirrors);
safe_put_page(conf->tmppage); safe_put_page(conf->tmppage);
kfree(conf->poolinfo); kfree(conf->poolinfo);
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
static int raid1_resize(struct mddev *mddev, sector_t sectors) static int raid1_resize(struct mddev *mddev, sector_t sectors)
...@@ -3181,7 +3153,7 @@ static struct md_personality raid1_personality = ...@@ -3181,7 +3153,7 @@ static struct md_personality raid1_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = raid1_free,
.status = status, .status = status,
.error_handler = error, .error_handler = error,
.hot_add_disk = raid1_add_disk, .hot_add_disk = raid1_add_disk,
...@@ -3193,6 +3165,8 @@ static struct md_personality raid1_personality = ...@@ -3193,6 +3165,8 @@ static struct md_personality raid1_personality =
.check_reshape = raid1_reshape, .check_reshape = raid1_reshape,
.quiesce = raid1_quiesce, .quiesce = raid1_quiesce,
.takeover = raid1_takeover, .takeover = raid1_takeover,
.congested = raid1_congested,
.mergeable_bvec = raid1_mergeable_bvec,
}; };
static int __init raid_init(void) static int __init raid_init(void)
......
...@@ -170,7 +170,4 @@ struct r1bio { ...@@ -170,7 +170,4 @@ struct r1bio {
*/ */
#define R1BIO_MadeGood 7 #define R1BIO_MadeGood 7
#define R1BIO_WriteError 8 #define R1BIO_WriteError 8
extern int md_raid1_congested(struct mddev *mddev, int bits);
#endif #endif
...@@ -674,7 +674,7 @@ static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev) ...@@ -674,7 +674,7 @@ static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev)
/** /**
* raid10_mergeable_bvec -- tell bio layer if a two requests can be merged * raid10_mergeable_bvec -- tell bio layer if a two requests can be merged
* @q: request queue * @mddev: the md device
* @bvm: properties of new bio * @bvm: properties of new bio
* @biovec: the request that could be merged to it. * @biovec: the request that could be merged to it.
* *
...@@ -682,11 +682,10 @@ static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev) ...@@ -682,11 +682,10 @@ static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev)
* This requires checking for end-of-chunk if near_copies != raid_disks, * This requires checking for end-of-chunk if near_copies != raid_disks,
* and for subordinate merge_bvec_fns if merge_check_needed. * and for subordinate merge_bvec_fns if merge_check_needed.
*/ */
static int raid10_mergeable_bvec(struct request_queue *q, static int raid10_mergeable_bvec(struct mddev *mddev,
struct bvec_merge_data *bvm, struct bvec_merge_data *bvm,
struct bio_vec *biovec) struct bio_vec *biovec)
{ {
struct mddev *mddev = q->queuedata;
struct r10conf *conf = mddev->private; struct r10conf *conf = mddev->private;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max; int max;
...@@ -910,7 +909,7 @@ static struct md_rdev *read_balance(struct r10conf *conf, ...@@ -910,7 +909,7 @@ static struct md_rdev *read_balance(struct r10conf *conf,
return rdev; return rdev;
} }
int md_raid10_congested(struct mddev *mddev, int bits) static int raid10_congested(struct mddev *mddev, int bits)
{ {
struct r10conf *conf = mddev->private; struct r10conf *conf = mddev->private;
int i, ret = 0; int i, ret = 0;
...@@ -934,15 +933,6 @@ int md_raid10_congested(struct mddev *mddev, int bits) ...@@ -934,15 +933,6 @@ int md_raid10_congested(struct mddev *mddev, int bits)
rcu_read_unlock(); rcu_read_unlock();
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(md_raid10_congested);
static int raid10_congested(void *data, int bits)
{
struct mddev *mddev = data;
return mddev_congested(mddev, bits) ||
md_raid10_congested(mddev, bits);
}
static void flush_pending_writes(struct r10conf *conf) static void flush_pending_writes(struct r10conf *conf)
{ {
...@@ -3757,8 +3747,6 @@ static int run(struct mddev *mddev) ...@@ -3757,8 +3747,6 @@ static int run(struct mddev *mddev)
if (mddev->queue) { if (mddev->queue) {
int stripe = conf->geo.raid_disks * int stripe = conf->geo.raid_disks *
((mddev->chunk_sectors << 9) / PAGE_SIZE); ((mddev->chunk_sectors << 9) / PAGE_SIZE);
mddev->queue->backing_dev_info.congested_fn = raid10_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
/* Calculate max read-ahead size. /* Calculate max read-ahead size.
* We need to readahead at least twice a whole stripe.... * We need to readahead at least twice a whole stripe....
...@@ -3767,7 +3755,6 @@ static int run(struct mddev *mddev) ...@@ -3767,7 +3755,6 @@ static int run(struct mddev *mddev)
stripe /= conf->geo.near_copies; stripe /= conf->geo.near_copies;
if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe) if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
mddev->queue->backing_dev_info.ra_pages = 2 * stripe; mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
} }
if (md_integrity_register(mddev)) if (md_integrity_register(mddev))
...@@ -3811,17 +3798,9 @@ static int run(struct mddev *mddev) ...@@ -3811,17 +3798,9 @@ static int run(struct mddev *mddev)
return -EIO; return -EIO;
} }
static int stop(struct mddev *mddev) static void raid10_free(struct mddev *mddev, void *priv)
{ {
struct r10conf *conf = mddev->private; struct r10conf *conf = priv;
raise_barrier(conf, 0);
lower_barrier(conf);
md_unregister_thread(&mddev->thread);
if (mddev->queue)
/* the unplug fn references 'conf'*/
blk_sync_queue(mddev->queue);
if (conf->r10bio_pool) if (conf->r10bio_pool)
mempool_destroy(conf->r10bio_pool); mempool_destroy(conf->r10bio_pool);
...@@ -3830,8 +3809,6 @@ static int stop(struct mddev *mddev) ...@@ -3830,8 +3809,6 @@ static int stop(struct mddev *mddev)
kfree(conf->mirrors_old); kfree(conf->mirrors_old);
kfree(conf->mirrors_new); kfree(conf->mirrors_new);
kfree(conf); kfree(conf);
mddev->private = NULL;
return 0;
} }
static void raid10_quiesce(struct mddev *mddev, int state) static void raid10_quiesce(struct mddev *mddev, int state)
...@@ -3895,7 +3872,7 @@ static int raid10_resize(struct mddev *mddev, sector_t sectors) ...@@ -3895,7 +3872,7 @@ static int raid10_resize(struct mddev *mddev, sector_t sectors)
return 0; return 0;
} }
static void *raid10_takeover_raid0(struct mddev *mddev) static void *raid10_takeover_raid0(struct mddev *mddev, sector_t size, int devs)
{ {
struct md_rdev *rdev; struct md_rdev *rdev;
struct r10conf *conf; struct r10conf *conf;
...@@ -3905,6 +3882,7 @@ static void *raid10_takeover_raid0(struct mddev *mddev) ...@@ -3905,6 +3882,7 @@ static void *raid10_takeover_raid0(struct mddev *mddev)
mdname(mddev)); mdname(mddev));
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
sector_div(size, devs);
/* Set new parameters */ /* Set new parameters */
mddev->new_level = 10; mddev->new_level = 10;
...@@ -3915,12 +3893,15 @@ static void *raid10_takeover_raid0(struct mddev *mddev) ...@@ -3915,12 +3893,15 @@ static void *raid10_takeover_raid0(struct mddev *mddev)
mddev->raid_disks *= 2; mddev->raid_disks *= 2;
/* make sure it will be not marked as dirty */ /* make sure it will be not marked as dirty */
mddev->recovery_cp = MaxSector; mddev->recovery_cp = MaxSector;
mddev->dev_sectors = size;
conf = setup_conf(mddev); conf = setup_conf(mddev);
if (!IS_ERR(conf)) { if (!IS_ERR(conf)) {
rdev_for_each(rdev, mddev) rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0) if (rdev->raid_disk >= 0) {
rdev->new_raid_disk = rdev->raid_disk * 2; rdev->new_raid_disk = rdev->raid_disk * 2;
rdev->sectors = size;
}
conf->barrier = 1; conf->barrier = 1;
} }
...@@ -3943,7 +3924,9 @@ static void *raid10_takeover(struct mddev *mddev) ...@@ -3943,7 +3924,9 @@ static void *raid10_takeover(struct mddev *mddev)
mdname(mddev)); mdname(mddev));
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
return raid10_takeover_raid0(mddev); return raid10_takeover_raid0(mddev,
raid0_conf->strip_zone->zone_end,
raid0_conf->strip_zone->nb_dev);
} }
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
...@@ -4713,7 +4696,7 @@ static struct md_personality raid10_personality = ...@@ -4713,7 +4696,7 @@ static struct md_personality raid10_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = raid10_free,
.status = status, .status = status,
.error_handler = error, .error_handler = error,
.hot_add_disk = raid10_add_disk, .hot_add_disk = raid10_add_disk,
...@@ -4727,6 +4710,8 @@ static struct md_personality raid10_personality = ...@@ -4727,6 +4710,8 @@ static struct md_personality raid10_personality =
.check_reshape = raid10_check_reshape, .check_reshape = raid10_check_reshape,
.start_reshape = raid10_start_reshape, .start_reshape = raid10_start_reshape,
.finish_reshape = raid10_finish_reshape, .finish_reshape = raid10_finish_reshape,
.congested = raid10_congested,
.mergeable_bvec = raid10_mergeable_bvec,
}; };
static int __init raid_init(void) static int __init raid_init(void)
......
...@@ -150,7 +150,4 @@ enum r10bio_state { ...@@ -150,7 +150,4 @@ enum r10bio_state {
*/ */
R10BIO_Previous, R10BIO_Previous,
}; };
extern int md_raid10_congested(struct mddev *mddev, int bits);
#endif #endif
...@@ -296,12 +296,9 @@ static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh, ...@@ -296,12 +296,9 @@ static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh,
BUG_ON(atomic_read(&conf->active_stripes)==0); BUG_ON(atomic_read(&conf->active_stripes)==0);
if (test_bit(STRIPE_HANDLE, &sh->state)) { if (test_bit(STRIPE_HANDLE, &sh->state)) {
if (test_bit(STRIPE_DELAYED, &sh->state) && if (test_bit(STRIPE_DELAYED, &sh->state) &&
!test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
list_add_tail(&sh->lru, &conf->delayed_list); list_add_tail(&sh->lru, &conf->delayed_list);
if (atomic_read(&conf->preread_active_stripes) else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
< IO_THRESHOLD)
md_wakeup_thread(conf->mddev->thread);
} else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
sh->bm_seq - conf->seq_write > 0) sh->bm_seq - conf->seq_write > 0)
list_add_tail(&sh->lru, &conf->bitmap_list); list_add_tail(&sh->lru, &conf->bitmap_list);
else { else {
...@@ -2898,31 +2895,102 @@ static int want_replace(struct stripe_head *sh, int disk_idx) ...@@ -2898,31 +2895,102 @@ static int want_replace(struct stripe_head *sh, int disk_idx)
* Returns 1 when no more member devices need to be checked, otherwise returns * Returns 1 when no more member devices need to be checked, otherwise returns
* 0 to tell the loop in handle_stripe_fill to continue * 0 to tell the loop in handle_stripe_fill to continue
*/ */
static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
int disk_idx, int disks) static int need_this_block(struct stripe_head *sh, struct stripe_head_state *s,
int disk_idx, int disks)
{ {
struct r5dev *dev = &sh->dev[disk_idx]; struct r5dev *dev = &sh->dev[disk_idx];
struct r5dev *fdev[2] = { &sh->dev[s->failed_num[0]], struct r5dev *fdev[2] = { &sh->dev[s->failed_num[0]],
&sh->dev[s->failed_num[1]] }; &sh->dev[s->failed_num[1]] };
int i;
if (test_bit(R5_LOCKED, &dev->flags) ||
test_bit(R5_UPTODATE, &dev->flags))
/* No point reading this as we already have it or have
* decided to get it.
*/
return 0;
if (dev->toread ||
(dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)))
/* We need this block to directly satisfy a request */
return 1;
if (s->syncing || s->expanding ||
(s->replacing && want_replace(sh, disk_idx)))
/* When syncing, or expanding we read everything.
* When replacing, we need the replaced block.
*/
return 1;
if ((s->failed >= 1 && fdev[0]->toread) ||
(s->failed >= 2 && fdev[1]->toread))
/* If we want to read from a failed device, then
* we need to actually read every other device.
*/
return 1;
/* Sometimes neither read-modify-write nor reconstruct-write
* cycles can work. In those cases we read every block we
* can. Then the parity-update is certain to have enough to
* work with.
* This can only be a problem when we need to write something,
* and some device has failed. If either of those tests
* fail we need look no further.
*/
if (!s->failed || !s->to_write)
return 0;
if (test_bit(R5_Insync, &dev->flags) &&
!test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
/* Pre-reads at not permitted until after short delay
* to gather multiple requests. However if this
* device is no Insync, the block could only be be computed
* and there is no need to delay that.
*/
return 0;
for (i = 0; i < s->failed; i++) {
if (fdev[i]->towrite &&
!test_bit(R5_UPTODATE, &fdev[i]->flags) &&
!test_bit(R5_OVERWRITE, &fdev[i]->flags))
/* If we have a partial write to a failed
* device, then we will need to reconstruct
* the content of that device, so all other
* devices must be read.
*/
return 1;
}
/* If we are forced to do a reconstruct-write, either because
* the current RAID6 implementation only supports that, or
* or because parity cannot be trusted and we are currently
* recovering it, there is extra need to be careful.
* If one of the devices that we would need to read, because
* it is not being overwritten (and maybe not written at all)
* is missing/faulty, then we need to read everything we can.
*/
if (sh->raid_conf->level != 6 &&
sh->sector < sh->raid_conf->mddev->recovery_cp)
/* reconstruct-write isn't being forced */
return 0;
for (i = 0; i < s->failed; i++) {
if (!test_bit(R5_UPTODATE, &fdev[i]->flags) &&
!test_bit(R5_OVERWRITE, &fdev[i]->flags))
return 1;
}
return 0;
}
static int fetch_block(struct stripe_head *sh, struct stripe_head_state *s,
int disk_idx, int disks)
{
struct r5dev *dev = &sh->dev[disk_idx];
/* is the data in this block needed, and can we get it? */ /* is the data in this block needed, and can we get it? */
if (!test_bit(R5_LOCKED, &dev->flags) && if (need_this_block(sh, s, disk_idx, disks)) {
!test_bit(R5_UPTODATE, &dev->flags) &&
(dev->toread ||
(dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
s->syncing || s->expanding ||
(s->replacing && want_replace(sh, disk_idx)) ||
(s->failed >= 1 && fdev[0]->toread) ||
(s->failed >= 2 && fdev[1]->toread) ||
(sh->raid_conf->level <= 5 && s->failed && fdev[0]->towrite &&
(!test_bit(R5_Insync, &dev->flags) || test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) &&
!test_bit(R5_OVERWRITE, &fdev[0]->flags)) ||
((sh->raid_conf->level == 6 ||
sh->sector >= sh->raid_conf->mddev->recovery_cp)
&& s->failed && s->to_write &&
(s->to_write - s->non_overwrite <
sh->raid_conf->raid_disks - sh->raid_conf->max_degraded) &&
(!test_bit(R5_Insync, &dev->flags) || test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))))) {
/* we would like to get this block, possibly by computing it, /* we would like to get this block, possibly by computing it,
* otherwise read it if the backing disk is insync * otherwise read it if the backing disk is insync
*/ */
...@@ -4081,7 +4149,7 @@ static void activate_bit_delay(struct r5conf *conf, ...@@ -4081,7 +4149,7 @@ static void activate_bit_delay(struct r5conf *conf,
} }
} }
int md_raid5_congested(struct mddev *mddev, int bits) static int raid5_congested(struct mddev *mddev, int bits)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf = mddev->private;
...@@ -4098,24 +4166,14 @@ int md_raid5_congested(struct mddev *mddev, int bits) ...@@ -4098,24 +4166,14 @@ int md_raid5_congested(struct mddev *mddev, int bits)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(md_raid5_congested);
static int raid5_congested(void *data, int bits)
{
struct mddev *mddev = data;
return mddev_congested(mddev, bits) ||
md_raid5_congested(mddev, bits);
}
/* We want read requests to align with chunks where possible, /* We want read requests to align with chunks where possible,
* but write requests don't need to. * but write requests don't need to.
*/ */
static int raid5_mergeable_bvec(struct request_queue *q, static int raid5_mergeable_bvec(struct mddev *mddev,
struct bvec_merge_data *bvm, struct bvec_merge_data *bvm,
struct bio_vec *biovec) struct bio_vec *biovec)
{ {
struct mddev *mddev = q->queuedata;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev); sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max; int max;
unsigned int chunk_sectors = mddev->chunk_sectors; unsigned int chunk_sectors = mddev->chunk_sectors;
...@@ -5296,11 +5354,14 @@ static void raid5d(struct md_thread *thread) ...@@ -5296,11 +5354,14 @@ static void raid5d(struct md_thread *thread)
static ssize_t static ssize_t
raid5_show_stripe_cache_size(struct mddev *mddev, char *page) raid5_show_stripe_cache_size(struct mddev *mddev, char *page)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
int ret = 0;
spin_lock(&mddev->lock);
conf = mddev->private;
if (conf) if (conf)
return sprintf(page, "%d\n", conf->max_nr_stripes); ret = sprintf(page, "%d\n", conf->max_nr_stripes);
else spin_unlock(&mddev->lock);
return 0; return ret;
} }
int int
...@@ -5339,21 +5400,25 @@ EXPORT_SYMBOL(raid5_set_cache_size); ...@@ -5339,21 +5400,25 @@ EXPORT_SYMBOL(raid5_set_cache_size);
static ssize_t static ssize_t
raid5_store_stripe_cache_size(struct mddev *mddev, const char *page, size_t len) raid5_store_stripe_cache_size(struct mddev *mddev, const char *page, size_t len)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
unsigned long new; unsigned long new;
int err; int err;
if (len >= PAGE_SIZE) if (len >= PAGE_SIZE)
return -EINVAL; return -EINVAL;
if (!conf)
return -ENODEV;
if (kstrtoul(page, 10, &new)) if (kstrtoul(page, 10, &new))
return -EINVAL; return -EINVAL;
err = raid5_set_cache_size(mddev, new); err = mddev_lock(mddev);
if (err) if (err)
return err; return err;
return len; conf = mddev->private;
if (!conf)
err = -ENODEV;
else
err = raid5_set_cache_size(mddev, new);
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry static struct md_sysfs_entry
...@@ -5364,29 +5429,40 @@ raid5_stripecache_size = __ATTR(stripe_cache_size, S_IRUGO | S_IWUSR, ...@@ -5364,29 +5429,40 @@ raid5_stripecache_size = __ATTR(stripe_cache_size, S_IRUGO | S_IWUSR,
static ssize_t static ssize_t
raid5_show_preread_threshold(struct mddev *mddev, char *page) raid5_show_preread_threshold(struct mddev *mddev, char *page)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
int ret = 0;
spin_lock(&mddev->lock);
conf = mddev->private;
if (conf) if (conf)
return sprintf(page, "%d\n", conf->bypass_threshold); ret = sprintf(page, "%d\n", conf->bypass_threshold);
else spin_unlock(&mddev->lock);
return 0; return ret;
} }
static ssize_t static ssize_t
raid5_store_preread_threshold(struct mddev *mddev, const char *page, size_t len) raid5_store_preread_threshold(struct mddev *mddev, const char *page, size_t len)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
unsigned long new; unsigned long new;
int err;
if (len >= PAGE_SIZE) if (len >= PAGE_SIZE)
return -EINVAL; return -EINVAL;
if (!conf)
return -ENODEV;
if (kstrtoul(page, 10, &new)) if (kstrtoul(page, 10, &new))
return -EINVAL; return -EINVAL;
if (new > conf->max_nr_stripes)
return -EINVAL; err = mddev_lock(mddev);
conf->bypass_threshold = new; if (err)
return len; return err;
conf = mddev->private;
if (!conf)
err = -ENODEV;
else if (new > conf->max_nr_stripes)
err = -EINVAL;
else
conf->bypass_threshold = new;
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry static struct md_sysfs_entry
...@@ -5398,39 +5474,48 @@ raid5_preread_bypass_threshold = __ATTR(preread_bypass_threshold, ...@@ -5398,39 +5474,48 @@ raid5_preread_bypass_threshold = __ATTR(preread_bypass_threshold,
static ssize_t static ssize_t
raid5_show_skip_copy(struct mddev *mddev, char *page) raid5_show_skip_copy(struct mddev *mddev, char *page)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
int ret = 0;
spin_lock(&mddev->lock);
conf = mddev->private;
if (conf) if (conf)
return sprintf(page, "%d\n", conf->skip_copy); ret = sprintf(page, "%d\n", conf->skip_copy);
else spin_unlock(&mddev->lock);
return 0; return ret;
} }
static ssize_t static ssize_t
raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len) raid5_store_skip_copy(struct mddev *mddev, const char *page, size_t len)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
unsigned long new; unsigned long new;
int err;
if (len >= PAGE_SIZE) if (len >= PAGE_SIZE)
return -EINVAL; return -EINVAL;
if (!conf)
return -ENODEV;
if (kstrtoul(page, 10, &new)) if (kstrtoul(page, 10, &new))
return -EINVAL; return -EINVAL;
new = !!new; new = !!new;
if (new == conf->skip_copy)
return len;
mddev_suspend(mddev); err = mddev_lock(mddev);
conf->skip_copy = new; if (err)
if (new) return err;
mddev->queue->backing_dev_info.capabilities |= conf = mddev->private;
BDI_CAP_STABLE_WRITES; if (!conf)
else err = -ENODEV;
mddev->queue->backing_dev_info.capabilities &= else if (new != conf->skip_copy) {
~BDI_CAP_STABLE_WRITES; mddev_suspend(mddev);
mddev_resume(mddev); conf->skip_copy = new;
return len; if (new)
mddev->queue->backing_dev_info.capabilities |=
BDI_CAP_STABLE_WRITES;
else
mddev->queue->backing_dev_info.capabilities &=
~BDI_CAP_STABLE_WRITES;
mddev_resume(mddev);
}
mddev_unlock(mddev);
return err ?: len;
} }
static struct md_sysfs_entry static struct md_sysfs_entry
...@@ -5454,11 +5539,14 @@ raid5_stripecache_active = __ATTR_RO(stripe_cache_active); ...@@ -5454,11 +5539,14 @@ raid5_stripecache_active = __ATTR_RO(stripe_cache_active);
static ssize_t static ssize_t
raid5_show_group_thread_cnt(struct mddev *mddev, char *page) raid5_show_group_thread_cnt(struct mddev *mddev, char *page)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
int ret = 0;
spin_lock(&mddev->lock);
conf = mddev->private;
if (conf) if (conf)
return sprintf(page, "%d\n", conf->worker_cnt_per_group); ret = sprintf(page, "%d\n", conf->worker_cnt_per_group);
else spin_unlock(&mddev->lock);
return 0; return ret;
} }
static int alloc_thread_groups(struct r5conf *conf, int cnt, static int alloc_thread_groups(struct r5conf *conf, int cnt,
...@@ -5468,7 +5556,7 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt, ...@@ -5468,7 +5556,7 @@ static int alloc_thread_groups(struct r5conf *conf, int cnt,
static ssize_t static ssize_t
raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf;
unsigned long new; unsigned long new;
int err; int err;
struct r5worker_group *new_groups, *old_groups; struct r5worker_group *new_groups, *old_groups;
...@@ -5476,41 +5564,41 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len) ...@@ -5476,41 +5564,41 @@ raid5_store_group_thread_cnt(struct mddev *mddev, const char *page, size_t len)
if (len >= PAGE_SIZE) if (len >= PAGE_SIZE)
return -EINVAL; return -EINVAL;
if (!conf)
return -ENODEV;
if (kstrtoul(page, 10, &new)) if (kstrtoul(page, 10, &new))
return -EINVAL; return -EINVAL;
if (new == conf->worker_cnt_per_group) err = mddev_lock(mddev);
return len; if (err)
return err;
mddev_suspend(mddev); conf = mddev->private;
if (!conf)
err = -ENODEV;
else if (new != conf->worker_cnt_per_group) {
mddev_suspend(mddev);
old_groups = conf->worker_groups; old_groups = conf->worker_groups;
if (old_groups) if (old_groups)
flush_workqueue(raid5_wq); flush_workqueue(raid5_wq);
err = alloc_thread_groups(conf, new, err = alloc_thread_groups(conf, new,
&group_cnt, &worker_cnt_per_group, &group_cnt, &worker_cnt_per_group,
&new_groups); &new_groups);
if (!err) { if (!err) {
spin_lock_irq(&conf->device_lock); spin_lock_irq(&conf->device_lock);
conf->group_cnt = group_cnt; conf->group_cnt = group_cnt;
conf->worker_cnt_per_group = worker_cnt_per_group; conf->worker_cnt_per_group = worker_cnt_per_group;
conf->worker_groups = new_groups; conf->worker_groups = new_groups;
spin_unlock_irq(&conf->device_lock); spin_unlock_irq(&conf->device_lock);
if (old_groups) if (old_groups)
kfree(old_groups[0].workers); kfree(old_groups[0].workers);
kfree(old_groups); kfree(old_groups);
}
mddev_resume(mddev);
} }
mddev_unlock(mddev);
mddev_resume(mddev); return err ?: len;
if (err)
return err;
return len;
} }
static struct md_sysfs_entry static struct md_sysfs_entry
...@@ -6178,11 +6266,6 @@ static int run(struct mddev *mddev) ...@@ -6178,11 +6266,6 @@ static int run(struct mddev *mddev)
if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe) if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
mddev->queue->backing_dev_info.ra_pages = 2 * stripe; mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
blk_queue_merge_bvec(mddev->queue, raid5_mergeable_bvec);
mddev->queue->backing_dev_info.congested_data = mddev;
mddev->queue->backing_dev_info.congested_fn = raid5_congested;
chunk_size = mddev->chunk_sectors << 9; chunk_size = mddev->chunk_sectors << 9;
blk_queue_io_min(mddev->queue, chunk_size); blk_queue_io_min(mddev->queue, chunk_size);
blk_queue_io_opt(mddev->queue, chunk_size * blk_queue_io_opt(mddev->queue, chunk_size *
...@@ -6260,17 +6343,12 @@ static int run(struct mddev *mddev) ...@@ -6260,17 +6343,12 @@ static int run(struct mddev *mddev)
return -EIO; return -EIO;
} }
static int stop(struct mddev *mddev) static void raid5_free(struct mddev *mddev, void *priv)
{ {
struct r5conf *conf = mddev->private; struct r5conf *conf = priv;
md_unregister_thread(&mddev->thread);
if (mddev->queue)
mddev->queue->backing_dev_info.congested_fn = NULL;
free_conf(conf); free_conf(conf);
mddev->private = NULL;
mddev->to_remove = &raid5_attrs_group; mddev->to_remove = &raid5_attrs_group;
return 0;
} }
static void status(struct seq_file *seq, struct mddev *mddev) static void status(struct seq_file *seq, struct mddev *mddev)
...@@ -7044,7 +7122,7 @@ static struct md_personality raid6_personality = ...@@ -7044,7 +7122,7 @@ static struct md_personality raid6_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = raid5_free,
.status = status, .status = status,
.error_handler = error, .error_handler = error,
.hot_add_disk = raid5_add_disk, .hot_add_disk = raid5_add_disk,
...@@ -7058,6 +7136,8 @@ static struct md_personality raid6_personality = ...@@ -7058,6 +7136,8 @@ static struct md_personality raid6_personality =
.finish_reshape = raid5_finish_reshape, .finish_reshape = raid5_finish_reshape,
.quiesce = raid5_quiesce, .quiesce = raid5_quiesce,
.takeover = raid6_takeover, .takeover = raid6_takeover,
.congested = raid5_congested,
.mergeable_bvec = raid5_mergeable_bvec,
}; };
static struct md_personality raid5_personality = static struct md_personality raid5_personality =
{ {
...@@ -7066,7 +7146,7 @@ static struct md_personality raid5_personality = ...@@ -7066,7 +7146,7 @@ static struct md_personality raid5_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = raid5_free,
.status = status, .status = status,
.error_handler = error, .error_handler = error,
.hot_add_disk = raid5_add_disk, .hot_add_disk = raid5_add_disk,
...@@ -7080,6 +7160,8 @@ static struct md_personality raid5_personality = ...@@ -7080,6 +7160,8 @@ static struct md_personality raid5_personality =
.finish_reshape = raid5_finish_reshape, .finish_reshape = raid5_finish_reshape,
.quiesce = raid5_quiesce, .quiesce = raid5_quiesce,
.takeover = raid5_takeover, .takeover = raid5_takeover,
.congested = raid5_congested,
.mergeable_bvec = raid5_mergeable_bvec,
}; };
static struct md_personality raid4_personality = static struct md_personality raid4_personality =
...@@ -7089,7 +7171,7 @@ static struct md_personality raid4_personality = ...@@ -7089,7 +7171,7 @@ static struct md_personality raid4_personality =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.make_request = make_request, .make_request = make_request,
.run = run, .run = run,
.stop = stop, .free = raid5_free,
.status = status, .status = status,
.error_handler = error, .error_handler = error,
.hot_add_disk = raid5_add_disk, .hot_add_disk = raid5_add_disk,
...@@ -7103,6 +7185,8 @@ static struct md_personality raid4_personality = ...@@ -7103,6 +7185,8 @@ static struct md_personality raid4_personality =
.finish_reshape = raid5_finish_reshape, .finish_reshape = raid5_finish_reshape,
.quiesce = raid5_quiesce, .quiesce = raid5_quiesce,
.takeover = raid4_takeover, .takeover = raid4_takeover,
.congested = raid5_congested,
.mergeable_bvec = raid5_mergeable_bvec,
}; };
static int __init raid5_init(void) static int __init raid5_init(void)
......
...@@ -558,7 +558,6 @@ static inline int algorithm_is_DDF(int layout) ...@@ -558,7 +558,6 @@ static inline int algorithm_is_DDF(int layout)
return layout >= 8 && layout <= 10; return layout >= 8 && layout <= 10;
} }
extern int md_raid5_congested(struct mddev *mddev, int bits);
extern void md_raid5_kick_device(struct r5conf *conf); extern void md_raid5_kick_device(struct r5conf *conf);
extern int raid5_set_cache_size(struct mddev *mddev, int size); extern int raid5_set_cache_size(struct mddev *mddev, int size);
#endif #endif
...@@ -89,10 +89,10 @@ void (*raid6_datap_recov)(int, size_t, int, void **); ...@@ -89,10 +89,10 @@ void (*raid6_datap_recov)(int, size_t, int, void **);
EXPORT_SYMBOL_GPL(raid6_datap_recov); EXPORT_SYMBOL_GPL(raid6_datap_recov);
const struct raid6_recov_calls *const raid6_recov_algos[] = { const struct raid6_recov_calls *const raid6_recov_algos[] = {
#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
#ifdef CONFIG_AS_AVX2 #ifdef CONFIG_AS_AVX2
&raid6_recov_avx2, &raid6_recov_avx2,
#endif #endif
#ifdef CONFIG_AS_SSSE3
&raid6_recov_ssse3, &raid6_recov_ssse3,
#endif #endif
&raid6_recov_intx1, &raid6_recov_intx1,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* of the License. * of the License.
*/ */
#if CONFIG_AS_AVX2 #ifdef CONFIG_AS_AVX2
#include <linux/raid/pq.h> #include <linux/raid/pq.h>
#include "x86.h" #include "x86.h"
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* of the License. * of the License.
*/ */
#ifdef CONFIG_AS_SSSE3
#include <linux/raid/pq.h> #include <linux/raid/pq.h>
#include "x86.h" #include "x86.h"
...@@ -330,3 +332,7 @@ const struct raid6_recov_calls raid6_recov_ssse3 = { ...@@ -330,3 +332,7 @@ const struct raid6_recov_calls raid6_recov_ssse3 = {
#endif #endif
.priority = 1, .priority = 1,
}; };
#else
#warning "your version of binutils lacks SSSE3 support"
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment