diff options
author | NeilBrown <neilb@suse.de> | 2009-03-30 23:39:39 -0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2009-03-30 23:39:39 -0400 |
commit | 245f46c2c221ef09c7db892f0e3fc2149be42052 (patch) | |
tree | be7e6f4b7b65b682c43e59ef17f7ed580bc8a82b /drivers | |
parent | 409c57f3801701dfee27a28103dda4831306cb20 (diff) |
md: add ->takeover method to support changing the personality managing an array
Implement this for RAID6 to be able to 'takeover' a RAID5 array. The
new RAID6 will use a layout which places Q on the last device, and
that device will be missing.
If there are any available spares, one will immediately have Q
recovered onto it.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/md/md.c | 101 | ||||
-rw-r--r-- | drivers/md/md.h | 10 | ||||
-rw-r--r-- | drivers/md/raid5.c | 64 |
3 files changed, 161 insertions, 14 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 6cb31f8da14c..05b613b5e4b2 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c | |||
@@ -2649,18 +2649,101 @@ level_show(mddev_t *mddev, char *page) | |||
2649 | static ssize_t | 2649 | static ssize_t |
2650 | level_store(mddev_t *mddev, const char *buf, size_t len) | 2650 | level_store(mddev_t *mddev, const char *buf, size_t len) |
2651 | { | 2651 | { |
2652 | char level[16]; | ||
2652 | ssize_t rv = len; | 2653 | ssize_t rv = len; |
2653 | if (mddev->pers) | 2654 | struct mdk_personality *pers; |
2655 | void *priv; | ||
2656 | |||
2657 | if (mddev->pers == NULL) { | ||
2658 | if (len == 0) | ||
2659 | return 0; | ||
2660 | if (len >= sizeof(mddev->clevel)) | ||
2661 | return -ENOSPC; | ||
2662 | strncpy(mddev->clevel, buf, len); | ||
2663 | if (mddev->clevel[len-1] == '\n') | ||
2664 | len--; | ||
2665 | mddev->clevel[len] = 0; | ||
2666 | mddev->level = LEVEL_NONE; | ||
2667 | return rv; | ||
2668 | } | ||
2669 | |||
2670 | /* request to change the personality. Need to ensure: | ||
2671 | * - array is not engaged in resync/recovery/reshape | ||
2672 | * - old personality can be suspended | ||
2673 | * - new personality will access other array. | ||
2674 | */ | ||
2675 | |||
2676 | if (mddev->sync_thread || mddev->reshape_position != MaxSector) | ||
2654 | return -EBUSY; | 2677 | return -EBUSY; |
2655 | if (len == 0) | 2678 | |
2656 | return 0; | 2679 | if (!mddev->pers->quiesce) { |
2657 | if (len >= sizeof(mddev->clevel)) | 2680 | printk(KERN_WARNING "md: %s: %s does not support online personality change\n", |
2658 | return -ENOSPC; | 2681 | mdname(mddev), mddev->pers->name); |
2659 | strncpy(mddev->clevel, buf, len); | 2682 | return -EINVAL; |
2660 | if (mddev->clevel[len-1] == '\n') | 2683 | } |
2684 | |||
2685 | /* Now find the new personality */ | ||
2686 | if (len == 0 || len >= sizeof(level)) | ||
2687 | return -EINVAL; | ||
2688 | strncpy(level, buf, len); | ||
2689 | if (level[len-1] == '\n') | ||
2661 | len--; | 2690 | len--; |
2662 | mddev->clevel[len] = 0; | 2691 | level[len] = 0; |
2663 | mddev->level = LEVEL_NONE; | 2692 | |
2693 | request_module("md-%s", level); | ||
2694 | spin_lock(&pers_lock); | ||
2695 | pers = find_pers(LEVEL_NONE, level); | ||
2696 | if (!pers || !try_module_get(pers->owner)) { | ||
2697 | spin_unlock(&pers_lock); | ||
2698 | printk(KERN_WARNING "md: personality %s not loaded\n", level); | ||
2699 | return -EINVAL; | ||
2700 | } | ||
2701 | spin_unlock(&pers_lock); | ||
2702 | |||
2703 | if (pers == mddev->pers) { | ||
2704 | /* Nothing to do! */ | ||
2705 | module_put(pers->owner); | ||
2706 | return rv; | ||
2707 | } | ||
2708 | if (!pers->takeover) { | ||
2709 | module_put(pers->owner); | ||
2710 | printk(KERN_WARNING "md: %s: %s does not support personality takeover\n", | ||
2711 | mdname(mddev), level); | ||
2712 | return -EINVAL; | ||
2713 | } | ||
2714 | |||
2715 | /* ->takeover must set new_* and/or delta_disks | ||
2716 | * if it succeeds, and may set them when it fails. | ||
2717 | */ | ||
2718 | priv = pers->takeover(mddev); | ||
2719 | if (IS_ERR(priv)) { | ||
2720 | mddev->new_level = mddev->level; | ||
2721 | mddev->new_layout = mddev->layout; | ||
2722 | mddev->new_chunk = mddev->chunk_size; | ||
2723 | mddev->raid_disks -= mddev->delta_disks; | ||
2724 | mddev->delta_disks = 0; | ||
2725 | module_put(pers->owner); | ||
2726 | printk(KERN_WARNING "md: %s: %s would not accept array\n", | ||
2727 | mdname(mddev), level); | ||
2728 | return PTR_ERR(priv); | ||
2729 | } | ||
2730 | |||
2731 | /* Looks like we have a winner */ | ||
2732 | mddev_suspend(mddev); | ||
2733 | mddev->pers->stop(mddev); | ||
2734 | module_put(mddev->pers->owner); | ||
2735 | mddev->pers = pers; | ||
2736 | mddev->private = priv; | ||
2737 | strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); | ||
2738 | mddev->level = mddev->new_level; | ||
2739 | mddev->layout = mddev->new_layout; | ||
2740 | mddev->chunk_size = mddev->new_chunk; | ||
2741 | mddev->delta_disks = 0; | ||
2742 | pers->run(mddev); | ||
2743 | mddev_resume(mddev); | ||
2744 | set_bit(MD_CHANGE_DEVS, &mddev->flags); | ||
2745 | set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); | ||
2746 | md_wakeup_thread(mddev->thread); | ||
2664 | return rv; | 2747 | return rv; |
2665 | } | 2748 | } |
2666 | 2749 | ||
diff --git a/drivers/md/md.h b/drivers/md/md.h index 84b22d67ba14..8034f62a9d28 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h | |||
@@ -321,6 +321,16 @@ struct mdk_personality | |||
321 | * others - reserved | 321 | * others - reserved |
322 | */ | 322 | */ |
323 | void (*quiesce) (mddev_t *mddev, int state); | 323 | void (*quiesce) (mddev_t *mddev, int state); |
324 | /* takeover is used to transition an array from one | ||
325 | * personality to another. The new personality must be able | ||
326 | * to handle the data in the current layout. | ||
327 | * e.g. 2drive raid1 -> 2drive raid5 | ||
328 | * ndrive raid5 -> degraded n+1drive raid6 with special layout | ||
329 | * If the takeover succeeds, a new 'private' structure is returned. | ||
330 | * This needs to be installed and then ->run used to activate the | ||
331 | * array. | ||
332 | */ | ||
333 | void *(*takeover) (mddev_t *mddev); | ||
324 | }; | 334 | }; |
325 | 335 | ||
326 | 336 | ||
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 81789fa7a023..5b346b41e515 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c | |||
@@ -933,8 +933,10 @@ static int grow_stripes(raid5_conf_t *conf, int num) | |||
933 | struct kmem_cache *sc; | 933 | struct kmem_cache *sc; |
934 | int devs = conf->raid_disks; | 934 | int devs = conf->raid_disks; |
935 | 935 | ||
936 | sprintf(conf->cache_name[0], "raid5-%s", mdname(conf->mddev)); | 936 | sprintf(conf->cache_name[0], |
937 | sprintf(conf->cache_name[1], "raid5-%s-alt", mdname(conf->mddev)); | 937 | "raid%d-%s", conf->level, mdname(conf->mddev)); |
938 | sprintf(conf->cache_name[1], | ||
939 | "raid%d-%s-alt", conf->level, mdname(conf->mddev)); | ||
938 | conf->active_name = 0; | 940 | conf->active_name = 0; |
939 | sc = kmem_cache_create(conf->cache_name[conf->active_name], | 941 | sc = kmem_cache_create(conf->cache_name[conf->active_name], |
940 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), | 942 | sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev), |
@@ -4361,10 +4363,12 @@ static int run(mddev_t *mddev) | |||
4361 | BUG_ON(mddev->chunk_size != mddev->new_chunk); | 4363 | BUG_ON(mddev->chunk_size != mddev->new_chunk); |
4362 | BUG_ON(mddev->delta_disks != 0); | 4364 | BUG_ON(mddev->delta_disks != 0); |
4363 | } | 4365 | } |
4364 | conf = setup_conf(mddev); | ||
4365 | 4366 | ||
4366 | if (conf == NULL) | 4367 | if (mddev->private == NULL) |
4367 | return -EIO; | 4368 | conf = setup_conf(mddev); |
4369 | else | ||
4370 | conf = mddev->private; | ||
4371 | |||
4368 | if (IS_ERR(conf)) | 4372 | if (IS_ERR(conf)) |
4369 | return PTR_ERR(conf); | 4373 | return PTR_ERR(conf); |
4370 | 4374 | ||
@@ -4880,6 +4884,55 @@ static void raid5_quiesce(mddev_t *mddev, int state) | |||
4880 | } | 4884 | } |
4881 | } | 4885 | } |
4882 | 4886 | ||
4887 | static struct mdk_personality raid5_personality; | ||
4888 | |||
4889 | static void *raid6_takeover(mddev_t *mddev) | ||
4890 | { | ||
4891 | /* Currently can only take over a raid5. We map the | ||
4892 | * personality to an equivalent raid6 personality | ||
4893 | * with the Q block at the end. | ||
4894 | */ | ||
4895 | int new_layout; | ||
4896 | |||
4897 | if (mddev->pers != &raid5_personality) | ||
4898 | return ERR_PTR(-EINVAL); | ||
4899 | if (mddev->degraded > 1) | ||
4900 | return ERR_PTR(-EINVAL); | ||
4901 | if (mddev->raid_disks > 253) | ||
4902 | return ERR_PTR(-EINVAL); | ||
4903 | if (mddev->raid_disks < 3) | ||
4904 | return ERR_PTR(-EINVAL); | ||
4905 | |||
4906 | switch (mddev->layout) { | ||
4907 | case ALGORITHM_LEFT_ASYMMETRIC: | ||
4908 | new_layout = ALGORITHM_LEFT_ASYMMETRIC_6; | ||
4909 | break; | ||
4910 | case ALGORITHM_RIGHT_ASYMMETRIC: | ||
4911 | new_layout = ALGORITHM_RIGHT_ASYMMETRIC_6; | ||
4912 | break; | ||
4913 | case ALGORITHM_LEFT_SYMMETRIC: | ||
4914 | new_layout = ALGORITHM_LEFT_SYMMETRIC_6; | ||
4915 | break; | ||
4916 | case ALGORITHM_RIGHT_SYMMETRIC: | ||
4917 | new_layout = ALGORITHM_RIGHT_SYMMETRIC_6; | ||
4918 | break; | ||
4919 | case ALGORITHM_PARITY_0: | ||
4920 | new_layout = ALGORITHM_PARITY_0_6; | ||
4921 | break; | ||
4922 | case ALGORITHM_PARITY_N: | ||
4923 | new_layout = ALGORITHM_PARITY_N; | ||
4924 | break; | ||
4925 | default: | ||
4926 | return ERR_PTR(-EINVAL); | ||
4927 | } | ||
4928 | mddev->new_level = 6; | ||
4929 | mddev->new_layout = new_layout; | ||
4930 | mddev->delta_disks = 1; | ||
4931 | mddev->raid_disks += 1; | ||
4932 | return setup_conf(mddev); | ||
4933 | } | ||
4934 | |||
4935 | |||
4883 | static struct mdk_personality raid6_personality = | 4936 | static struct mdk_personality raid6_personality = |
4884 | { | 4937 | { |
4885 | .name = "raid6", | 4938 | .name = "raid6", |
@@ -4900,6 +4953,7 @@ static struct mdk_personality raid6_personality = | |||
4900 | .start_reshape = raid5_start_reshape, | 4953 | .start_reshape = raid5_start_reshape, |
4901 | #endif | 4954 | #endif |
4902 | .quiesce = raid5_quiesce, | 4955 | .quiesce = raid5_quiesce, |
4956 | .takeover = raid6_takeover, | ||
4903 | }; | 4957 | }; |
4904 | static struct mdk_personality raid5_personality = | 4958 | static struct mdk_personality raid5_personality = |
4905 | { | 4959 | { |