md/bitmap: protect against bitmap removal while being updated.
authorNeilBrown <neilb@suse.de>
Mon, 14 Dec 2009 01:49:46 +0000 (12:49 +1100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 18 Dec 2009 21:43:25 +0000 (13:43 -0800)
commit aa5cbd103887011b4830355f88fb055f9ad2d556 upstream.

A write intent bitmap can be removed from an array while the
array is active.
When this happens, all IO is suspended and flushed before the
bitmap is removed.
However it is possible that bitmap_daemon_work is still running to
clear old bits from the bitmap.  If it is, it can dereference the
bitmap after it has been freed.

So introduce a new mutex to protect bitmap_daemon_work and get it
before destroying a bitmap.

This is suitable for any current -stable kernel.

Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/md/bitmap.c
drivers/md/bitmap.h
drivers/md/md.c
drivers/md/md.h

index 3319c2fec28e40f7908d7e0688595c113f3a597f..0aee97a30b80a035ff9d080af34ed143fbf10a55 100644 (file)
@@ -1077,23 +1077,31 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
  *                     out to disk
  */
 
-void bitmap_daemon_work(struct bitmap *bitmap)
+void bitmap_daemon_work(mddev_t *mddev)
 {
+       struct bitmap *bitmap;
        unsigned long j;
        unsigned long flags;
        struct page *page = NULL, *lastpage = NULL;
        int blocks;
        void *paddr;
 
-       if (bitmap == NULL)
+       /* Use a mutex to guard daemon_work against
+        * bitmap_destroy.
+        */
+       mutex_lock(&mddev->bitmap_mutex);
+       bitmap = mddev->bitmap;
+       if (bitmap == NULL) {
+               mutex_unlock(&mddev->bitmap_mutex);
                return;
+       }
        if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ))
                goto done;
 
        bitmap->daemon_lastrun = jiffies;
        if (bitmap->allclean) {
                bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
-               return;
+               goto done;
        }
        bitmap->allclean = 1;
 
@@ -1202,6 +1210,7 @@ void bitmap_daemon_work(struct bitmap *bitmap)
  done:
        if (bitmap->allclean == 0)
                bitmap->mddev->thread->timeout = bitmap->daemon_sleep * HZ;
+       mutex_unlock(&mddev->bitmap_mutex);
 }
 
 static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
@@ -1538,9 +1547,9 @@ void bitmap_flush(mddev_t *mddev)
         */
        sleep = bitmap->daemon_sleep;
        bitmap->daemon_sleep = 0;
-       bitmap_daemon_work(bitmap);
-       bitmap_daemon_work(bitmap);
-       bitmap_daemon_work(bitmap);
+       bitmap_daemon_work(mddev);
+       bitmap_daemon_work(mddev);
+       bitmap_daemon_work(mddev);
        bitmap->daemon_sleep = sleep;
        bitmap_update_sb(bitmap);
 }
@@ -1571,6 +1580,7 @@ static void bitmap_free(struct bitmap *bitmap)
        kfree(bp);
        kfree(bitmap);
 }
+
 void bitmap_destroy(mddev_t *mddev)
 {
        struct bitmap *bitmap = mddev->bitmap;
@@ -1578,7 +1588,9 @@ void bitmap_destroy(mddev_t *mddev)
        if (!bitmap) /* there was no bitmap */
                return;
 
+       mutex_lock(&mddev->bitmap_mutex);
        mddev->bitmap = NULL; /* disconnect from the md device */
+       mutex_unlock(&mddev->bitmap_mutex);
        if (mddev->thread)
                mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
 
index e98900671ca91d7a4f76ae91ac7393f6613bd5ee..7e38d13ddcacce54b47206f191af739ce84d436c 100644 (file)
@@ -282,7 +282,7 @@ void bitmap_close_sync(struct bitmap *bitmap);
 void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector);
 
 void bitmap_unplug(struct bitmap *bitmap);
-void bitmap_daemon_work(struct bitmap *bitmap);
+void bitmap_daemon_work(mddev_t *mddev);
 #endif
 
 #endif
index 78871cd974604e5a898d85bd940341944c9f83b1..5ff27aa86a504c5df4970916388bc71d71b7c88f 100644 (file)
@@ -361,6 +361,7 @@ static mddev_t * mddev_find(dev_t unit)
 
        mutex_init(&new->open_mutex);
        mutex_init(&new->reconfig_mutex);
+       mutex_init(&new->bitmap_mutex);
        INIT_LIST_HEAD(&new->disks);
        INIT_LIST_HEAD(&new->all_mddevs);
        init_timer(&new->safemode_timer);
@@ -6595,7 +6596,7 @@ void md_check_recovery(mddev_t *mddev)
 
 
        if (mddev->bitmap)
-               bitmap_daemon_work(mddev->bitmap);
+               bitmap_daemon_work(mddev);
 
        if (mddev->ro)
                return;
index f8fc188bc762d4b6fe1ffb5dadc9ac43a36d6dff..d7aad830018e8a5bfda58b4013ffbc2b3a64ab74 100644 (file)
@@ -289,6 +289,7 @@ struct mddev_s
                                                                * hot-adding a bitmap.  It should
                                                                * eventually be settable by sysfs.
                                                                */
+       struct mutex                    bitmap_mutex;
 
        struct list_head                all_mddevs;
 };