]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/md/md.c
Support adding a spare to a live md array with external metadata.
[linux-2.6-omap-h63xx.git] / drivers / md / md.c
index 51c19f86ff99c3785b5d27838e755d04dabe5fe3..5d6fac1fd39ebbaa05c522d78bcd997d08758018 100644 (file)
@@ -276,7 +276,9 @@ static mddev_t * mddev_find(dev_t unit)
        atomic_set(&new->active, 1);
        spin_lock_init(&new->write_lock);
        init_waitqueue_head(&new->sb_wait);
+       init_waitqueue_head(&new->recovery_wait);
        new->reshape_position = MaxSector;
+       new->resync_min = 0;
        new->resync_max = MaxSector;
        new->level = LEVEL_NONE;
 
@@ -1930,7 +1932,7 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                slot = -1;
        else if (e==buf || (*e && *e!= '\n'))
                return -EINVAL;
-       if (rdev->mddev->pers) {
+       if (rdev->mddev->pers && slot == -1) {
                /* Setting 'slot' on an active array requires also
                 * updating the 'rd%d' link, and communicating
                 * with the personality with ->hot_*_disk.
@@ -1938,8 +1940,6 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                 * failed/spare devices.  This normally happens automatically,
                 * but not when the metadata is externally managed.
                 */
-               if (slot != -1)
-                       return -EBUSY;
                if (rdev->raid_disk == -1)
                        return -EEXIST;
                /* personality does all needed checks */
@@ -1953,6 +1953,44 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
                sysfs_remove_link(&rdev->mddev->kobj, nm);
                set_bit(MD_RECOVERY_NEEDED, &rdev->mddev->recovery);
                md_wakeup_thread(rdev->mddev->thread);
+       } else if (rdev->mddev->pers) {
+               mdk_rdev_t *rdev2;
+               struct list_head *tmp;
+               /* Activating a spare .. or possibly reactivating
+                * if we every get bitmaps working here.
+                */
+
+               if (rdev->raid_disk != -1)
+                       return -EBUSY;
+
+               if (rdev->mddev->pers->hot_add_disk == NULL)
+                       return -EINVAL;
+
+               rdev_for_each(rdev2, tmp, rdev->mddev)
+                       if (rdev2->raid_disk == slot)
+                               return -EEXIST;
+
+               rdev->raid_disk = slot;
+               if (test_bit(In_sync, &rdev->flags))
+                       rdev->saved_raid_disk = slot;
+               else
+                       rdev->saved_raid_disk = -1;
+               err = rdev->mddev->pers->
+                       hot_add_disk(rdev->mddev, rdev);
+               if (err != 1) {
+                       rdev->raid_disk = -1;
+                       if (err == 0)
+                               return -EEXIST;
+                       return err;
+               }
+               sprintf(nm, "rd%d", rdev->raid_disk);
+               if (sysfs_create_link(&rdev->mddev->kobj, &rdev->kobj, nm))
+                       printk(KERN_WARNING
+                              "md: cannot register "
+                              "%s for %s\n",
+                              nm, mdname(rdev->mddev));
+
+               /* don't wakeup anyone, leave that to userspace. */
        } else {
                if (slot >= rdev->mddev->raid_disks)
                        return -ENOSPC;
@@ -1982,7 +2020,7 @@ offset_store(mdk_rdev_t *rdev, const char *buf, size_t len)
        unsigned long long offset = simple_strtoull(buf, &e, 10);
        if (e==buf || (*e && *e != '\n'))
                return -EINVAL;
-       if (rdev->mddev->pers)
+       if (rdev->mddev->pers && rdev->raid_disk >= 0)
                return -EBUSY;
        if (rdev->size && rdev->mddev->external)
                /* Must set offset before size, so overlap checks
@@ -2021,7 +2059,7 @@ rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len)
 
        if (e==buf || (*e && *e != '\n'))
                return -EINVAL;
-       if (my_mddev->pers)
+       if (my_mddev->pers && rdev->raid_disk >= 0)
                return -EBUSY;
        rdev->size = size;
        if (size > oldsize && rdev->mddev->external) {
@@ -3073,6 +3111,36 @@ sync_completed_show(mddev_t *mddev, char *page)
 
 static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
 
+static ssize_t
+min_sync_show(mddev_t *mddev, char *page)
+{
+       return sprintf(page, "%llu\n",
+                      (unsigned long long)mddev->resync_min);
+}
+static ssize_t
+min_sync_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       unsigned long long min;
+       if (strict_strtoull(buf, 10, &min))
+               return -EINVAL;
+       if (min > mddev->resync_max)
+               return -EINVAL;
+       if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
+               return -EBUSY;
+
+       /* Must be a multiple of chunk_size */
+       if (mddev->chunk_size) {
+               if (min & (sector_t)((mddev->chunk_size>>9)-1))
+                       return -EINVAL;
+       }
+       mddev->resync_min = min;
+
+       return len;
+}
+
+static struct md_sysfs_entry md_min_sync =
+__ATTR(sync_min, S_IRUGO|S_IWUSR, min_sync_show, min_sync_store);
+
 static ssize_t
 max_sync_show(mddev_t *mddev, char *page)
 {
@@ -3088,9 +3156,10 @@ max_sync_store(mddev_t *mddev, const char *buf, size_t len)
        if (strncmp(buf, "max", 3) == 0)
                mddev->resync_max = MaxSector;
        else {
-               char *ep;
-               unsigned long long max = simple_strtoull(buf, &ep, 10);
-               if (ep == buf || (*ep != 0 && *ep != '\n'))
+               unsigned long long max;
+               if (strict_strtoull(buf, 10, &max))
+                       return -EINVAL;
+               if (max < mddev->resync_min)
                        return -EINVAL;
                if (max < mddev->resync_max &&
                    test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
@@ -3221,6 +3290,7 @@ static struct attribute *md_redundancy_attrs[] = {
        &md_sync_speed.attr,
        &md_sync_force_parallel.attr,
        &md_sync_completed.attr,
+       &md_min_sync.attr,
        &md_max_sync.attr,
        &md_suspend_lo.attr,
        &md_suspend_hi.attr,
@@ -3325,9 +3395,9 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
        disk->queue = mddev->queue;
        add_disk(disk);
        mddev->gendisk = disk;
-       mutex_unlock(&disks_mutex);
        error = kobject_init_and_add(&mddev->kobj, &md_ktype, &disk->dev.kobj,
                                     "%s", "md");
+       mutex_unlock(&disks_mutex);
        if (error)
                printk(KERN_WARNING "md: cannot register %s/md - name in use\n",
                       disk->disk_name);
@@ -3776,6 +3846,7 @@ static int do_md_stop(mddev_t * mddev, int mode)
                mddev->size = 0;
                mddev->raid_disks = 0;
                mddev->recovery_cp = 0;
+               mddev->resync_min = 0;
                mddev->resync_max = MaxSector;
                mddev->reshape_position = MaxSector;
                mddev->external = 0;
@@ -3896,8 +3967,10 @@ static void autorun_devices(int part)
 
                md_probe(dev, NULL, NULL);
                mddev = mddev_find(dev);
-               if (!mddev) {
-                       printk(KERN_ERR 
+               if (!mddev || !mddev->gendisk) {
+                       if (mddev)
+                               mddev_put(mddev);
+                       printk(KERN_ERR
                                "md: cannot allocate memory for md drive.\n");
                        break;
                }
@@ -5499,6 +5572,8 @@ void md_allow_write(mddev_t *mddev)
                return;
        if (mddev->ro)
                return;
+       if (!mddev->pers->sync_request)
+               return;
 
        spin_lock_irq(&mddev->write_lock);
        if (mddev->in_sync) {
@@ -5622,9 +5697,11 @@ void md_do_sync(mddev_t *mddev)
                max_sectors = mddev->resync_max_sectors;
                mddev->resync_mismatches = 0;
                /* we don't use the checkpoint if there's a bitmap */
-               if (!mddev->bitmap &&
-                   !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+               if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+                       j = mddev->resync_min;
+               else if (!mddev->bitmap)
                        j = mddev->recovery_cp;
+
        } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
                max_sectors = mddev->size << 1;
        else {
@@ -5665,7 +5742,6 @@ void md_do_sync(mddev_t *mddev)
                window/2,(unsigned long long) max_sectors/2);
 
        atomic_set(&mddev->recovery_active, 0);
-       init_waitqueue_head(&mddev->recovery_wait);
        last_check = 0;
 
        if (j>2) {
@@ -5794,6 +5870,7 @@ void md_do_sync(mddev_t *mddev)
 
  skip:
        mddev->curr_resync = 0;
+       mddev->resync_min = 0;
        mddev->resync_max = MaxSector;
        sysfs_notify(&mddev->kobj, NULL, "sync_completed");
        wake_up(&resync_wait);