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/md/md.c | |
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/md/md.c')
-rw-r--r-- | drivers/md/md.c | 101 |
1 files changed, 92 insertions, 9 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 | ||