md/bitmap: protect against bitmap removal while being updated.
authorNeilBrown <neilb@suse.de>
Mon, 14 Dec 2009 01:49:46 +0000 (12:49 +1100)
committerNeilBrown <neilb@suse.de>
Mon, 14 Dec 2009 01:49:46 +0000 (12:49 +1100)
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>
Cc: stable@kernel.org
drivers/md/bitmap.c
drivers/md/bitmap.h
drivers/md/md.c
drivers/md/md.h

index 60e2b322db110b96d52f66e4f502610b29d822fe..a5e5f2fbf96351baca4fb443a472f1f58692d379 100644 (file)
@@ -1078,23 +1078,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;
 
@@ -1203,6 +1211,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,
@@ -1541,9 +1550,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);
 }
@@ -1574,6 +1583,7 @@ static void bitmap_free(struct bitmap *bitmap)
        kfree(bp);
        kfree(bitmap);
 }
+
 void bitmap_destroy(mddev_t *mddev)
 {
        struct bitmap *bitmap = mddev->bitmap;
@@ -1581,7 +1591,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 5f154ef1e4befeeef26358d9ae37e424c62cee47..1be7a16a7a53898196b4b9f03894e755e73d1894 100644 (file)
@@ -363,6 +363,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);
@@ -6625,7 +6626,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 f184b69ef337514d460d669ffc4309a7d5dcc515..87430fea28752fa40486de40e2563d838368b1fc 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;
 };