Commit aa5cbd10 authored by NeilBrown's avatar NeilBrown

md/bitmap: protect against bitmap removal while being updated.

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: default avatarNeilBrown <neilb@suse.de>
Cc: stable@kernel.org
parent f4054253
...@@ -1078,23 +1078,31 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, ...@@ -1078,23 +1078,31 @@ static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
* out to disk * 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 j;
unsigned long flags; unsigned long flags;
struct page *page = NULL, *lastpage = NULL; struct page *page = NULL, *lastpage = NULL;
int blocks; int blocks;
void *paddr; 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; return;
}
if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ)) if (time_before(jiffies, bitmap->daemon_lastrun + bitmap->daemon_sleep*HZ))
goto done; goto done;
bitmap->daemon_lastrun = jiffies; bitmap->daemon_lastrun = jiffies;
if (bitmap->allclean) { if (bitmap->allclean) {
bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; bitmap->mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
return; goto done;
} }
bitmap->allclean = 1; bitmap->allclean = 1;
...@@ -1203,6 +1211,7 @@ void bitmap_daemon_work(struct bitmap *bitmap) ...@@ -1203,6 +1211,7 @@ void bitmap_daemon_work(struct bitmap *bitmap)
done: done:
if (bitmap->allclean == 0) if (bitmap->allclean == 0)
bitmap->mddev->thread->timeout = bitmap->daemon_sleep * HZ; bitmap->mddev->thread->timeout = bitmap->daemon_sleep * HZ;
mutex_unlock(&mddev->bitmap_mutex);
} }
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap, static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
...@@ -1541,9 +1550,9 @@ void bitmap_flush(mddev_t *mddev) ...@@ -1541,9 +1550,9 @@ void bitmap_flush(mddev_t *mddev)
*/ */
sleep = bitmap->daemon_sleep; sleep = bitmap->daemon_sleep;
bitmap->daemon_sleep = 0; bitmap->daemon_sleep = 0;
bitmap_daemon_work(bitmap); bitmap_daemon_work(mddev);
bitmap_daemon_work(bitmap); bitmap_daemon_work(mddev);
bitmap_daemon_work(bitmap); bitmap_daemon_work(mddev);
bitmap->daemon_sleep = sleep; bitmap->daemon_sleep = sleep;
bitmap_update_sb(bitmap); bitmap_update_sb(bitmap);
} }
...@@ -1574,6 +1583,7 @@ static void bitmap_free(struct bitmap *bitmap) ...@@ -1574,6 +1583,7 @@ static void bitmap_free(struct bitmap *bitmap)
kfree(bp); kfree(bp);
kfree(bitmap); kfree(bitmap);
} }
void bitmap_destroy(mddev_t *mddev) void bitmap_destroy(mddev_t *mddev)
{ {
struct bitmap *bitmap = mddev->bitmap; struct bitmap *bitmap = mddev->bitmap;
...@@ -1581,7 +1591,9 @@ void bitmap_destroy(mddev_t *mddev) ...@@ -1581,7 +1591,9 @@ void bitmap_destroy(mddev_t *mddev)
if (!bitmap) /* there was no bitmap */ if (!bitmap) /* there was no bitmap */
return; return;
mutex_lock(&mddev->bitmap_mutex);
mddev->bitmap = NULL; /* disconnect from the md device */ mddev->bitmap = NULL; /* disconnect from the md device */
mutex_unlock(&mddev->bitmap_mutex);
if (mddev->thread) if (mddev->thread)
mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT; mddev->thread->timeout = MAX_SCHEDULE_TIMEOUT;
......
...@@ -282,7 +282,7 @@ void bitmap_close_sync(struct bitmap *bitmap); ...@@ -282,7 +282,7 @@ void bitmap_close_sync(struct bitmap *bitmap);
void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector); void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector);
void bitmap_unplug(struct bitmap *bitmap); void bitmap_unplug(struct bitmap *bitmap);
void bitmap_daemon_work(struct bitmap *bitmap); void bitmap_daemon_work(mddev_t *mddev);
#endif #endif
#endif #endif
...@@ -363,6 +363,7 @@ static mddev_t * mddev_find(dev_t unit) ...@@ -363,6 +363,7 @@ static mddev_t * mddev_find(dev_t unit)
mutex_init(&new->open_mutex); mutex_init(&new->open_mutex);
mutex_init(&new->reconfig_mutex); mutex_init(&new->reconfig_mutex);
mutex_init(&new->bitmap_mutex);
INIT_LIST_HEAD(&new->disks); INIT_LIST_HEAD(&new->disks);
INIT_LIST_HEAD(&new->all_mddevs); INIT_LIST_HEAD(&new->all_mddevs);
init_timer(&new->safemode_timer); init_timer(&new->safemode_timer);
...@@ -6625,7 +6626,7 @@ void md_check_recovery(mddev_t *mddev) ...@@ -6625,7 +6626,7 @@ void md_check_recovery(mddev_t *mddev)
if (mddev->bitmap) if (mddev->bitmap)
bitmap_daemon_work(mddev->bitmap); bitmap_daemon_work(mddev);
if (mddev->ro) if (mddev->ro)
return; return;
......
...@@ -289,6 +289,7 @@ struct mddev_s ...@@ -289,6 +289,7 @@ struct mddev_s
* hot-adding a bitmap. It should * hot-adding a bitmap. It should
* eventually be settable by sysfs. * eventually be settable by sysfs.
*/ */
struct mutex bitmap_mutex;
struct list_head all_mddevs; struct list_head all_mddevs;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment