aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/md/md.c101
-rw-r--r--drivers/md/md.h10
-rw-r--r--drivers/md/raid5.c64
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)
2649static ssize_t 2649static ssize_t
2650level_store(mddev_t *mddev, const char *buf, size_t len) 2650level_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
4887static struct mdk_personality raid5_personality;
4888
4889static 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
4883static struct mdk_personality raid6_personality = 4936static 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};
4904static struct mdk_personality raid5_personality = 4958static struct mdk_personality raid5_personality =
4905{ 4959{