md: remove unneeded sysfs files more promptly
[safe/jmp/linux-2.6] / drivers / md / md.c
index c4bdf06..edf777f 100644 (file)
@@ -507,9 +507,32 @@ static inline int mddev_trylock(mddev_t * mddev)
        return mutex_trylock(&mddev->reconfig_mutex);
 }
 
+static struct attribute_group md_redundancy_group;
+
 static inline void mddev_unlock(mddev_t * mddev)
 {
-       mutex_unlock(&mddev->reconfig_mutex);
+       if (mddev->pers == NULL && mddev->private) {
+               /* These cannot be removed under reconfig_mutex as
+                * an access to the files will try to take reconfig_mutex
+                * while holding the file unremovable, which leads to
+                * a deadlock.
+                * So hold open_mutex instead - we are allowed to take
+                * it while holding reconfig_mutex, and md_run can
+                * use it to wait for the remove to complete.
+                */
+               mutex_lock(&mddev->open_mutex);
+               mutex_unlock(&mddev->reconfig_mutex);
+
+               sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
+               if (mddev->private != (void*)1)
+                       sysfs_remove_group(&mddev->kobj, mddev->private);
+               if (mddev->sysfs_action)
+                       sysfs_put(mddev->sysfs_action);
+               mddev->sysfs_action = NULL;
+               mddev->private = NULL;
+               mutex_unlock(&mddev->open_mutex);
+       } else
+               mutex_unlock(&mddev->reconfig_mutex);
 
        md_wakeup_thread(mddev->thread);
 }
@@ -4075,13 +4098,6 @@ static void mddev_delayed_delete(struct work_struct *ws)
 {
        mddev_t *mddev = container_of(ws, mddev_t, del_work);
 
-       if (mddev->private == &md_redundancy_group) {
-               sysfs_remove_group(&mddev->kobj, &md_redundancy_group);
-               if (mddev->sysfs_action)
-                       sysfs_put(mddev->sysfs_action);
-               mddev->sysfs_action = NULL;
-               mddev->private = NULL;
-       }
        sysfs_remove_group(&mddev->kobj, &md_bitmap_group);
        kobject_del(&mddev->kobj);
        kobject_put(&mddev->kobj);
@@ -4239,6 +4255,13 @@ static int do_md_run(mddev_t * mddev)
        if (mddev->pers)
                return -EBUSY;
 
+       /* These two calls synchronise us with the
+        * sysfs_remove_group calls in mddev_unlock,
+        * so they must have completed.
+        */
+       mutex_lock(&mddev->open_mutex);
+       mutex_unlock(&mddev->open_mutex);
+
        /*
         * Analyze all RAID superblock(s)
         */
@@ -4287,10 +4310,7 @@ static int do_md_run(mddev_t * mddev)
                sysfs_notify_dirent(rdev->sysfs_state);
        }
 
-       md_probe(mddev->unit, NULL, NULL);
        disk = mddev->gendisk;
-       if (!disk)
-               return -ENOMEM;
 
        spin_lock(&pers_lock);
        pers = find_pers(mddev->level, mddev->clevel);
@@ -4530,8 +4550,8 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
                        mddev->queue->unplug_fn = NULL;
                        mddev->queue->backing_dev_info.congested_fn = NULL;
                        module_put(mddev->pers->owner);
-                       if (mddev->pers->sync_request)
-                               mddev->private = &md_redundancy_group;
+                       if (mddev->pers->sync_request && mddev->private == NULL)
+                               mddev->private = (void*)1;
                        mddev->pers = NULL;
                        /* tell userspace to handle 'inactive' */
                        sysfs_notify_dirent(mddev->sysfs_state);
@@ -4578,9 +4598,6 @@ out:
                }
                mddev->bitmap_info.offset = 0;
 
-               /* make sure all md_delayed_delete calls have finished */
-               flush_scheduled_work();
-
                export_array(mddev);
 
                mddev->array_sectors = 0;
@@ -5493,6 +5510,7 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
        int err = 0;
        void __user *argp = (void __user *)arg;
        mddev_t *mddev = NULL;
+       int ro;
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -5628,6 +5646,34 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
                        err = do_md_stop(mddev, 1, 1);
                        goto done_unlock;
 
+               case BLKROSET:
+                       if (get_user(ro, (int __user *)(arg))) {
+                               err = -EFAULT;
+                               goto done_unlock;
+                       }
+                       err = -EINVAL;
+
+                       /* if the bdev is going readonly the value of mddev->ro
+                        * does not matter, no writes are coming
+                        */
+                       if (ro)
+                               goto done_unlock;
+
+                       /* are we are already prepared for writes? */
+                       if (mddev->ro != 1)
+                               goto done_unlock;
+
+                       /* transitioning to readauto need only happen for
+                        * arrays that call md_write_start
+                        */
+                       if (mddev->pers) {
+                               err = restart_array(mddev);
+                               if (err == 0) {
+                                       mddev->ro = 2;
+                                       set_disk_ro(mddev->gendisk, 0);
+                               }
+                       }
+                       goto done_unlock;
        }
 
        /*
@@ -6473,10 +6519,11 @@ void md_do_sync(mddev_t *mddev)
                mddev->curr_resync = 2;
 
        try_again:
-               if (kthread_should_stop()) {
+               if (kthread_should_stop())
                        set_bit(MD_RECOVERY_INTR, &mddev->recovery);
+
+               if (test_bit(MD_RECOVERY_INTR, &mddev->recovery))
                        goto skip;
-               }
                for_each_mddev(mddev2, tmp) {
                        if (mddev2 == mddev)
                                continue;