[PATCH] md: Change case of raid level reported in sys/mdX/md/level
[deliverable/linux.git] / drivers / md / md.c
index 8238c1c0ec096c88de90555944b602ff2bc9ac0b..8175a2a222da8fc847f7a48f220eea7768414fb9 100644 (file)
@@ -131,6 +131,8 @@ static ctl_table raid_root_table[] = {
 
 static struct block_device_operations md_fops;
 
+static int start_readonly;
+
 /*
  * Enables to iterate over all existing md arrays
  * all_mddevs_lock protects this list.
@@ -1026,7 +1028,6 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
                mddev->size = le64_to_cpu(sb->size)/2;
                mddev->events = le64_to_cpu(sb->events);
                mddev->bitmap_offset = 0;
-               mddev->default_bitmap_offset = 0;
                mddev->default_bitmap_offset = 1024;
                
                mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
@@ -1180,6 +1181,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
 {
        mdk_rdev_t *same_pdev;
        char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE];
+       struct kobject *ko;
 
        if (rdev->mddev) {
                MD_BUG();
@@ -1219,7 +1221,11 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev)
        rdev->kobj.parent = &mddev->kobj;
        kobject_add(&rdev->kobj);
 
-       sysfs_create_link(&rdev->kobj, &rdev->bdev->bd_disk->kobj, "block");
+       if (rdev->bdev->bd_part)
+               ko = &rdev->bdev->bd_part->kobj;
+       else
+               ko = &rdev->bdev->bd_disk->kobj;
+       sysfs_create_link(&rdev->kobj, ko, "block");
        return 0;
 }
 
@@ -1497,7 +1503,7 @@ struct rdev_sysfs_entry {
 };
 
 static ssize_t
-rdev_show_state(mdk_rdev_t *rdev, char *page)
+state_show(mdk_rdev_t *rdev, char *page)
 {
        char *sep = "";
        int len=0;
@@ -1518,13 +1524,11 @@ rdev_show_state(mdk_rdev_t *rdev, char *page)
        return len+sprintf(page+len, "\n");
 }
 
-static struct rdev_sysfs_entry rdev_state = {
-       .attr = {.name = "state", .mode = S_IRUGO },
-       .show = rdev_show_state,
-};
+static struct rdev_sysfs_entry
+rdev_state = __ATTR_RO(state);
 
 static ssize_t
-rdev_show_super(mdk_rdev_t *rdev, char *page)
+super_show(mdk_rdev_t *rdev, char *page)
 {
        if (rdev->sb_loaded && rdev->sb_size) {
                memcpy(page, page_address(rdev->sb_page), rdev->sb_size);
@@ -1532,10 +1536,8 @@ rdev_show_super(mdk_rdev_t *rdev, char *page)
        } else
                return 0;
 }
-static struct rdev_sysfs_entry rdev_super = {
-       .attr = {.name = "super", .mode = S_IRUGO },
-       .show = rdev_show_super,
-};
+static struct rdev_sysfs_entry rdev_super = __ATTR_RO(super);
+
 static struct attribute *rdev_default_attrs[] = {
        &rdev_state.attr,
        &rdev_super.attr,
@@ -1721,37 +1723,33 @@ static void analyze_sbs(mddev_t * mddev)
 }
 
 static ssize_t
-md_show_level(mddev_t *mddev, char *page)
+level_show(mddev_t *mddev, char *page)
 {
        mdk_personality_t *p = mddev->pers;
-       if (p == NULL)
+       if (p == NULL && mddev->raid_disks == 0)
                return 0;
        if (mddev->level >= 0)
-               return sprintf(page, "RAID-%d\n", mddev->level);
+               return sprintf(page, "raid%d\n", mddev->level);
        else
                return sprintf(page, "%s\n", p->name);
 }
 
-static struct md_sysfs_entry md_level = {
-       .attr = {.name = "level", .mode = S_IRUGO },
-       .show = md_show_level,
-};
+static struct md_sysfs_entry md_level = __ATTR_RO(level);
 
 static ssize_t
-md_show_rdisks(mddev_t *mddev, char *page)
+raid_disks_show(mddev_t *mddev, char *page)
 {
+       if (mddev->raid_disks == 0)
+               return 0;
        return sprintf(page, "%d\n", mddev->raid_disks);
 }
 
-static struct md_sysfs_entry md_raid_disks = {
-       .attr = {.name = "raid_disks", .mode = S_IRUGO },
-       .show = md_show_rdisks,
-};
+static struct md_sysfs_entry md_raid_disks = __ATTR_RO(raid_disks);
 
 static ssize_t
-md_show_scan(mddev_t *mddev, char *page)
+action_show(mddev_t *mddev, char *page)
 {
-       char *type = "none";
+       char *type = "idle";
        if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
            test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) {
                if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
@@ -1768,66 +1766,84 @@ md_show_scan(mddev_t *mddev, char *page)
 }
 
 static ssize_t
-md_store_scan(mddev_t *mddev, const char *page, size_t len)
+action_store(mddev_t *mddev, const char *page, size_t len)
 {
-       int canscan=0;
+       if (!mddev->pers || !mddev->pers->sync_request)
+               return -EINVAL;
+
+       if (strcmp(page, "idle")==0 || strcmp(page, "idle\n")==0) {
+               if (mddev->sync_thread) {
+                       set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+                       md_unregister_thread(mddev->sync_thread);
+                       mddev->sync_thread = NULL;
+                       mddev->recovery = 0;
+               }
+               return len;
+       }
 
        if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
            test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
                return -EBUSY;
-       down(&mddev->reconfig_sem);
-       if (mddev->pers && mddev->pers->sync_request)
-               canscan=1;
-       up(&mddev->reconfig_sem);
-       if (!canscan)
-               return -EINVAL;
-
-       if (strcmp(page, "check")==0 || strcmp(page, "check\n")==0)
-               set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
-       else if (strcmp(page, "repair")!=0 && strcmp(page, "repair\n")!=0)
-               return -EINVAL;
-       set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
-       set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
-       set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       if (strcmp(page, "resync")==0 || strcmp(page, "resync\n")==0 ||
+           strcmp(page, "recover")==0 || strcmp(page, "recover\n")==0)
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       else {
+               if (strcmp(page, "check")==0 || strcmp(page, "check\n")==0)
+                       set_bit(MD_RECOVERY_CHECK, &mddev->recovery);
+               else if (strcmp(page, "repair")!=0 && strcmp(page, "repair\n")!=0)
+                       return -EINVAL;
+               set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
+               set_bit(MD_RECOVERY_SYNC, &mddev->recovery);
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+       }
        md_wakeup_thread(mddev->thread);
        return len;
 }
 
 static ssize_t
-md_show_mismatch(mddev_t *mddev, char *page)
+mismatch_cnt_show(mddev_t *mddev, char *page)
 {
        return sprintf(page, "%llu\n",
                       (unsigned long long) mddev->resync_mismatches);
 }
 
-static struct md_sysfs_entry md_scan_mode = {
-       .attr = {.name = "scan_mode", .mode = S_IRUGO|S_IWUSR },
-       .show = md_show_scan,
-       .store = md_store_scan,
-};
+static struct md_sysfs_entry
+md_scan_mode = __ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store);
 
-static struct md_sysfs_entry md_mismatches = {
-       .attr = {.name = "mismatch_cnt", .mode = S_IRUGO },
-       .show = md_show_mismatch,
-};
+
+static struct md_sysfs_entry
+md_mismatches = __ATTR_RO(mismatch_cnt);
 
 static struct attribute *md_default_attrs[] = {
        &md_level.attr,
        &md_raid_disks.attr,
+       NULL,
+};
+
+static struct attribute *md_redundancy_attrs[] = {
        &md_scan_mode.attr,
        &md_mismatches.attr,
        NULL,
 };
+static struct attribute_group md_redundancy_group = {
+       .name = NULL,
+       .attrs = md_redundancy_attrs,
+};
+
 
 static ssize_t
 md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
 {
        struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
        mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
+       ssize_t rv;
 
        if (!entry->show)
                return -EIO;
-       return entry->show(mddev, page);
+       mddev_lock(mddev);
+       rv = entry->show(mddev, page);
+       mddev_unlock(mddev);
+       return rv;
 }
 
 static ssize_t
@@ -1836,10 +1852,14 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
 {
        struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr);
        mddev_t *mddev = container_of(kobj, struct mddev_s, kobj);
+       ssize_t rv;
 
        if (!entry->store)
                return -EIO;
-       return entry->store(mddev, page, length);
+       mddev_lock(mddev);
+       rv = entry->store(mddev, page, length);
+       mddev_unlock(mddev);
+       return rv;
 }
 
 static void md_free(struct kobject *ko)
@@ -2029,6 +2049,9 @@ static int do_md_run(mddev_t * mddev)
        mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */
        mddev->barriers_work = 1;
 
+       if (start_readonly)
+               mddev->ro = 2; /* read-only, but switch on first write */
+
        /* before we start the array running, initialise the bitmap */
        err = bitmap_create(mddev);
        if (err)
@@ -2043,6 +2066,11 @@ static int do_md_run(mddev_t * mddev)
                bitmap_destroy(mddev);
                return err;
        }
+       if (mddev->pers->sync_request)
+               sysfs_create_group(&mddev->kobj, &md_redundancy_group);
+       else if (mddev->ro == 2) /* auto-readonly not meaningful */
+               mddev->ro = 0;
+
        atomic_set(&mddev->writes_pending,0);
        mddev->safemode = 0;
        mddev->safemode_timer.function = md_safemode_timeout;
@@ -2141,7 +2169,7 @@ static int do_md_stop(mddev_t * mddev, int ro)
 
                if (ro) {
                        err  = -ENXIO;
-                       if (mddev->ro)
+                       if (mddev->ro==1)
                                goto out;
                        mddev->ro = 1;
                } else {
@@ -2151,6 +2179,9 @@ static int do_md_stop(mddev_t * mddev, int ro)
                                set_disk_ro(disk, 0);
                        blk_queue_make_request(mddev->queue, md_fail_request);
                        mddev->pers->stop(mddev);
+                       if (mddev->pers->sync_request)
+                               sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
+
                        module_put(mddev->pers->owner);
                        mddev->pers = NULL;
                        if (mddev->ro)
@@ -2900,6 +2931,9 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
 
        mddev->sb_dirty      = 1;
 
+       mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
+       mddev->bitmap_offset = 0;
+
        /*
         * Generate a 128 bit UUID
         */
@@ -3124,7 +3158,7 @@ static int md_ioctl(struct inode *inode, struct file *file,
                if (cnt > 0 ) {
                        printk(KERN_WARNING
                               "md: %s(pid %d) used deprecated START_ARRAY ioctl. "
-                              "This will not be supported beyond 2.6\n",
+                              "This will not be supported beyond July 2006\n",
                               current->comm, current->pid);
                        cnt--;
                }
@@ -3258,12 +3292,22 @@ static int md_ioctl(struct inode *inode, struct file *file,
 
        /*
         * The remaining ioctls are changing the state of the
-        * superblock, so we do not allow read-only arrays
-        * here:
+        * superblock, so we do not allow them on read-only arrays.
+        * However non-MD ioctls (e.g. get-size) will still come through
+        * here and hit the 'default' below, so only disallow
+        * 'md' ioctls, and switch to rw mode if started auto-readonly.
         */
-       if (mddev->ro) {
-               err = -EROFS;
-               goto abort_unlock;
+       if (_IOC_TYPE(cmd) == MD_MAJOR &&
+           mddev->ro && mddev->pers) {
+               if (mddev->ro == 2) {
+                       mddev->ro = 0;
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+               md_wakeup_thread(mddev->thread);
+
+               } else {
+                       err = -EROFS;
+                       goto abort_unlock;
+               }
        }
 
        switch (cmd)
@@ -3393,21 +3437,26 @@ static int md_thread(void * arg)
         */
 
        allow_signal(SIGKILL);
-       complete(thread->event);
        while (!kthread_should_stop()) {
-               void (*run)(mddev_t *);
 
-               wait_event_interruptible_timeout(thread->wqueue,
-                                                test_bit(THREAD_WAKEUP, &thread->flags)
-                                                || kthread_should_stop(),
-                                                thread->timeout);
+               /* We need to wait INTERRUPTIBLE so that
+                * we don't add to the load-average.
+                * That means we need to be sure no signals are
+                * pending
+                */
+               if (signal_pending(current))
+                       flush_signals(current);
+
+               wait_event_interruptible_timeout
+                       (thread->wqueue,
+                        test_bit(THREAD_WAKEUP, &thread->flags)
+                        || kthread_should_stop(),
+                        thread->timeout);
                try_to_freeze();
 
                clear_bit(THREAD_WAKEUP, &thread->flags);
 
-               run = thread->run;
-               if (run)
-                       run(thread->mddev);
+               thread->run(thread->mddev);
        }
 
        return 0;
@@ -3426,7 +3475,6 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
                                 const char *name)
 {
        mdk_thread_t *thread;
-       struct completion event;
 
        thread = kmalloc(sizeof(mdk_thread_t), GFP_KERNEL);
        if (!thread)
@@ -3435,18 +3483,14 @@ mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev,
        memset(thread, 0, sizeof(mdk_thread_t));
        init_waitqueue_head(&thread->wqueue);
 
-       init_completion(&event);
-       thread->event = &event;
        thread->run = run;
        thread->mddev = mddev;
-       thread->name = name;
        thread->timeout = MAX_SCHEDULE_TIMEOUT;
        thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev));
        if (IS_ERR(thread->tsk)) {
                kfree(thread);
                return NULL;
        }
-       wait_for_completion(&event);
        return thread;
 }
 
@@ -3651,8 +3695,10 @@ static int md_seq_show(struct seq_file *seq, void *v)
                seq_printf(seq, "%s : %sactive", mdname(mddev),
                                                mddev->pers ? "" : "in");
                if (mddev->pers) {
-                       if (mddev->ro)
+                       if (mddev->ro==1)
                                seq_printf(seq, " (read-only)");
+                       if (mddev->ro==2)
+                               seq_printf(seq, "(auto-read-only)");
                        seq_printf(seq, " %s", mddev->pers->name);
                }
 
@@ -3692,11 +3738,15 @@ static int md_seq_show(struct seq_file *seq, void *v)
                if (mddev->pers) {
                        mddev->pers->status (seq, mddev);
                        seq_printf(seq, "\n      ");
-                       if (mddev->curr_resync > 2) {
-                               status_resync (seq, mddev);
-                               seq_printf(seq, "\n      ");
-                       } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2)
-                               seq_printf(seq, "       resync=DELAYED\n      ");
+                       if (mddev->pers->sync_request) {
+                               if (mddev->curr_resync > 2) {
+                                       status_resync (seq, mddev);
+                                       seq_printf(seq, "\n      ");
+                               } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2)
+                                       seq_printf(seq, "\tresync=DELAYED\n      ");
+                               else if (mddev->recovery_cp < MaxSector)
+                                       seq_printf(seq, "\tresync=PENDING\n      ");
+                       }
                } else
                        seq_printf(seq, "\n       ");
 
@@ -3798,11 +3848,20 @@ static int is_mddev_idle(mddev_t *mddev)
                curr_events = disk_stat_read(disk, sectors[0]) + 
                                disk_stat_read(disk, sectors[1]) - 
                                atomic_read(&disk->sync_io);
-               /* Allow some slack between valud of curr_events and last_events,
-                * as there are some uninteresting races.
+               /* The difference between curr_events and last_events
+                * will be affected by any new non-sync IO (making
+                * curr_events bigger) and any difference in the amount of
+                * in-flight syncio (making current_events bigger or smaller)
+                * The amount in-flight is currently limited to
+                * 32*64K in raid1/10 and 256*PAGE_SIZE in raid5/6
+                * which is at most 4096 sectors.
+                * These numbers are fairly fragile and should be made
+                * more robust, probably by enforcing the
+                * 'window size' that md_do_sync sort-of uses.
+                *
                 * Note: the following is an unsigned comparison.
                 */
-               if ((curr_events - rdev->last_events + 32) > 64) {
+               if ((curr_events - rdev->last_events + 4096) > 8192) {
                        rdev->last_events = curr_events;
                        idle = 0;
                }
@@ -3833,6 +3892,13 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
        if (bio_data_dir(bi) != WRITE)
                return;
 
+       BUG_ON(mddev->ro == 1);
+       if (mddev->ro == 2) {
+               /* need to switch to read/write */
+               mddev->ro = 0;
+               set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+               md_wakeup_thread(mddev->thread);
+       }
        atomic_inc(&mddev->writes_pending);
        if (mddev->in_sync) {
                spin_lock_irq(&mddev->write_lock);
@@ -3897,9 +3963,7 @@ static void md_do_sync(mddev_t *mddev)
                mddev->curr_resync = 2;
 
        try_again:
-               if (signal_pending(current) ||
-                   kthread_should_stop()) {
-                       flush_signals(current);
+               if (kthread_should_stop()) {
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        goto skip;
                }
@@ -3919,9 +3983,8 @@ static void md_do_sync(mddev_t *mddev)
                                         * time 'round when curr_resync == 2
                                         */
                                        continue;
-                               prepare_to_wait(&resync_wait, &wq, TASK_INTERRUPTIBLE);
-                               if (!signal_pending(current) &&
-                                   !kthread_should_stop() &&
+                               prepare_to_wait(&resync_wait, &wq, TASK_UNINTERRUPTIBLE);
+                               if (!kthread_should_stop() &&
                                    mddev2->curr_resync >= mddev->curr_resync) {
                                        printk(KERN_INFO "md: delaying resync of %s"
                                               " until %s has finished resync (they"
@@ -4030,13 +4093,12 @@ static void md_do_sync(mddev_t *mddev)
                }
 
 
-               if (signal_pending(current) || kthread_should_stop()) {
+               if (kthread_should_stop()) {
                        /*
                         * got a signal, exit.
                         */
                        printk(KERN_INFO 
                                "md: md_do_sync() got signal ... exiting\n");
-                       flush_signals(current);
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
                        goto out;
                }
@@ -4058,7 +4120,7 @@ static void md_do_sync(mddev_t *mddev)
                if (currspeed > sysctl_speed_limit_min) {
                        if ((currspeed > sysctl_speed_limit_max) ||
                                        !is_mddev_idle(mddev)) {
-                               msleep_interruptible(250);
+                               msleep(500);
                                goto repeat;
                        }
                }
@@ -4431,6 +4493,23 @@ static __exit void md_exit(void)
 module_init(md_init)
 module_exit(md_exit)
 
+static int get_ro(char *buffer, struct kernel_param *kp)
+{
+       return sprintf(buffer, "%d", start_readonly);
+}
+static int set_ro(const char *val, struct kernel_param *kp)
+{
+       char *e;
+       int num = simple_strtoul(val, &e, 10);
+       if (*val && (*e == '\0' || *e == '\n')) {
+               start_readonly = num;
+               return 0;;
+       }
+       return -EINVAL;
+}
+
+module_param_call(start_ro, set_ro, get_ro, NULL, 0600);
+
 EXPORT_SYMBOL(register_md_personality);
 EXPORT_SYMBOL(unregister_md_personality);
 EXPORT_SYMBOL(md_error);
This page took 0.033409 seconds and 5 git commands to generate.