Commit e384e585 authored by NeilBrown's avatar NeilBrown
Browse files

md/bitmap: prepare for storing write-intent-bitmap via dm-dirty-log.



This allows md/raid5 to fully work as a dm target.

Normally md uses a 'filemap' which contains a list of pages of bits
each of which may be written separately.
dm-log uses and all-or-nothing approach to writing the log, so
when using a dm-log, ->filemap is NULL and the flags normally stored
in filemap_attr are stored in ->logattrs instead.



Signed-off-by: default avatarNeilBrown <neilb@suse.de>
parent ef425673
Loading
Loading
Loading
Loading
+89 −39
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@
#include "md.h"
#include "md.h"
#include "bitmap.h"
#include "bitmap.h"


#include <linux/dm-dirty-log.h>
/* debug macros */
/* debug macros */


#define DEBUG 0
#define DEBUG 0
@@ -694,6 +695,8 @@ static inline unsigned long file_page_offset(struct bitmap *bitmap, unsigned lon
static inline struct page *filemap_get_page(struct bitmap *bitmap,
static inline struct page *filemap_get_page(struct bitmap *bitmap,
					unsigned long chunk)
					unsigned long chunk)
{
{
	if (bitmap->filemap == NULL)
		return NULL;
	if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
	if (file_page_index(bitmap, chunk) >= bitmap->file_pages)
		return NULL;
		return NULL;
	return bitmap->filemap[file_page_index(bitmap, chunk)
	return bitmap->filemap[file_page_index(bitmap, chunk)
@@ -793,19 +796,28 @@ enum bitmap_page_attr {
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
				enum bitmap_page_attr attr)
				enum bitmap_page_attr attr)
{
{
	if (page)
		__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
		__set_bit((page->index<<2) + attr, bitmap->filemap_attr);
	else
		__set_bit(attr, &bitmap->logattrs);
}
}


static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
				enum bitmap_page_attr attr)
				enum bitmap_page_attr attr)
{
{
	if (page)
		__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
		__clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
	else
		__clear_bit(attr, &bitmap->logattrs);
}
}


static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
					   enum bitmap_page_attr attr)
					   enum bitmap_page_attr attr)
{
{
	if (page)
		return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
		return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
	else
		return test_bit(attr, &bitmap->logattrs);
}
}


/*
/*
@@ -818,12 +830,15 @@ static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *p
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
{
{
	unsigned long bit;
	unsigned long bit;
	struct page *page;
	struct page *page = NULL;
	void *kaddr;
	void *kaddr;
	unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
	unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);


	if (!bitmap->filemap)
	if (!bitmap->filemap) {
		return;
		struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
		if (log)
			log->type->mark_region(log, chunk);
	} else {


		page = filemap_get_page(bitmap, chunk);
		page = filemap_get_page(bitmap, chunk);
		if (!page)
		if (!page)
@@ -838,7 +853,7 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
			ext2_set_bit(bit, kaddr);
			ext2_set_bit(bit, kaddr);
		kunmap_atomic(kaddr, KM_USER0);
		kunmap_atomic(kaddr, KM_USER0);
		PRINTK("set file bit %lu page %lu\n", bit, page->index);
		PRINTK("set file bit %lu page %lu\n", bit, page->index);

	}
	/* record page number so it gets flushed to disk when unplug occurs */
	/* record page number so it gets flushed to disk when unplug occurs */
	set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
	set_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
}
}
@@ -855,6 +870,16 @@ void bitmap_unplug(struct bitmap *bitmap)


	if (!bitmap)
	if (!bitmap)
		return;
		return;
	if (!bitmap->filemap) {
		/* Must be using a dirty_log */
		struct dm_dirty_log *log = bitmap->mddev->bitmap_info.log;
		dirty = test_and_clear_bit(BITMAP_PAGE_DIRTY, &bitmap->logattrs);
		need_write = test_and_clear_bit(BITMAP_PAGE_NEEDWRITE, &bitmap->logattrs);
		if (dirty || need_write)
			if (log->type->flush(log))
				bitmap->flags |= BITMAP_WRITE_ERROR;
		goto out;
	}


	/* look at each page to see if there are any set bits that need to be
	/* look at each page to see if there are any set bits that need to be
	 * flushed out to disk */
	 * flushed out to disk */
@@ -883,6 +908,7 @@ void bitmap_unplug(struct bitmap *bitmap)
		else
		else
			md_super_wait(bitmap->mddev);
			md_super_wait(bitmap->mddev);
	}
	}
out:
	if (bitmap->flags & BITMAP_WRITE_ERROR)
	if (bitmap->flags & BITMAP_WRITE_ERROR)
		bitmap_file_kick(bitmap);
		bitmap_file_kick(bitmap);
}
}
@@ -925,11 +951,11 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
		printk(KERN_INFO "%s: bitmap file is out of date, doing full "
		printk(KERN_INFO "%s: bitmap file is out of date, doing full "
			"recovery\n", bmname(bitmap));
			"recovery\n", bmname(bitmap));


	bytes = (chunks + 7) / 8;
	bytes = DIV_ROUND_UP(bitmap->chunks, 8);
	if (!bitmap->mddev->bitmap_info.external)
	if (!bitmap->mddev->bitmap_info.external)
		bytes += sizeof(bitmap_super_t);
		bytes += sizeof(bitmap_super_t);


	num_pages = (bytes + PAGE_SIZE - 1) / PAGE_SIZE;
	num_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);


	if (file && i_size_read(file->f_mapping->host) < bytes) {
	if (file && i_size_read(file->f_mapping->host) < bytes) {
		printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
		printk(KERN_INFO "%s: bitmap file too short %lu < %lu\n",
@@ -1090,6 +1116,7 @@ void bitmap_daemon_work(mddev_t *mddev)
	struct page *page = NULL, *lastpage = NULL;
	struct page *page = NULL, *lastpage = NULL;
	int blocks;
	int blocks;
	void *paddr;
	void *paddr;
	struct dm_dirty_log *log = mddev->bitmap_info.log;


	/* Use a mutex to guard daemon_work against
	/* Use a mutex to guard daemon_work against
	 * bitmap_destroy.
	 * bitmap_destroy.
@@ -1114,10 +1141,11 @@ void bitmap_daemon_work(mddev_t *mddev)
	spin_lock_irqsave(&bitmap->lock, flags);
	spin_lock_irqsave(&bitmap->lock, flags);
	for (j = 0; j < bitmap->chunks; j++) {
	for (j = 0; j < bitmap->chunks; j++) {
		bitmap_counter_t *bmc;
		bitmap_counter_t *bmc;
		if (!bitmap->filemap)
		if (!bitmap->filemap) {
			if (!log)
				/* error or shutdown */
				/* error or shutdown */
				break;
				break;

		} else
			page = filemap_get_page(bitmap, j);
			page = filemap_get_page(bitmap, j);


		if (page != lastpage) {
		if (page != lastpage) {
@@ -1187,6 +1215,7 @@ void bitmap_daemon_work(mddev_t *mddev)
						  -1);
						  -1);


				/* clear the bit */
				/* clear the bit */
				if (page) {
					paddr = kmap_atomic(page, KM_USER0);
					paddr = kmap_atomic(page, KM_USER0);
					if (bitmap->flags & BITMAP_HOSTENDIAN)
					if (bitmap->flags & BITMAP_HOSTENDIAN)
						clear_bit(file_page_offset(bitmap, j),
						clear_bit(file_page_offset(bitmap, j),
@@ -1195,6 +1224,8 @@ void bitmap_daemon_work(mddev_t *mddev)
						ext2_clear_bit(file_page_offset(bitmap, j),
						ext2_clear_bit(file_page_offset(bitmap, j),
							       paddr);
							       paddr);
					kunmap_atomic(paddr, KM_USER0);
					kunmap_atomic(paddr, KM_USER0);
				} else
					log->type->clear_region(log, j);
			}
			}
		} else
		} else
			j |= PAGE_COUNTER_MASK;
			j |= PAGE_COUNTER_MASK;
@@ -1202,12 +1233,16 @@ void bitmap_daemon_work(mddev_t *mddev)
	spin_unlock_irqrestore(&bitmap->lock, flags);
	spin_unlock_irqrestore(&bitmap->lock, flags);


	/* now sync the final page */
	/* now sync the final page */
	if (lastpage != NULL) {
	if (lastpage != NULL || log != NULL) {
		spin_lock_irqsave(&bitmap->lock, flags);
		spin_lock_irqsave(&bitmap->lock, flags);
		if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
		if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
			clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
			clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
			spin_unlock_irqrestore(&bitmap->lock, flags);
			spin_unlock_irqrestore(&bitmap->lock, flags);
			if (lastpage)
				write_page(bitmap, lastpage, 0);
				write_page(bitmap, lastpage, 0);
			else
				if (log->type->flush(log))
					bitmap->flags |= BITMAP_WRITE_ERROR;
		} else {
		} else {
			set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
			set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
			spin_unlock_irqrestore(&bitmap->lock, flags);
			spin_unlock_irqrestore(&bitmap->lock, flags);
@@ -1372,7 +1407,9 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
		(*bmc)--;
		(*bmc)--;
		if (*bmc <= 2)
		if (*bmc <= 2)
			set_page_attr(bitmap,
			set_page_attr(bitmap,
				      filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
				      filemap_get_page(
					      bitmap,
					      offset >> CHUNK_BLOCK_SHIFT(bitmap)),
				      BITMAP_PAGE_CLEAN);
				      BITMAP_PAGE_CLEAN);


		spin_unlock_irqrestore(&bitmap->lock, flags);
		spin_unlock_irqrestore(&bitmap->lock, flags);
@@ -1649,10 +1686,13 @@ int bitmap_create(mddev_t *mddev)


	BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);
	BUILD_BUG_ON(sizeof(bitmap_super_t) != 256);


	if (!file && !mddev->bitmap_info.offset) /* bitmap disabled, nothing to do */
	if (!file
	    && !mddev->bitmap_info.offset
	    && !mddev->bitmap_info.log) /* bitmap disabled, nothing to do */
		return 0;
		return 0;


	BUG_ON(file && mddev->bitmap_info.offset);
	BUG_ON(file && mddev->bitmap_info.offset);
	BUG_ON(mddev->bitmap_info.offset && mddev->bitmap_info.log);


	bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
	bitmap = kzalloc(sizeof(*bitmap), GFP_KERNEL);
	if (!bitmap)
	if (!bitmap)
@@ -1730,6 +1770,16 @@ int bitmap_create(mddev_t *mddev)
	    || bitmap->events_cleared == mddev->events)
	    || bitmap->events_cleared == mddev->events)
		/* no need to keep dirty bits to optimise a re-add of a missing device */
		/* no need to keep dirty bits to optimise a re-add of a missing device */
		start = mddev->recovery_cp;
		start = mddev->recovery_cp;
	if (mddev->bitmap_info.log) {
		unsigned long i;
		struct dm_dirty_log *log = mddev->bitmap_info.log;
		for (i = 0; i < bitmap->chunks; i++)
			if (!log->type->in_sync(log, i, 1))
				bitmap_set_memory_bits(bitmap,
						       (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
						       1);
		err = 0;
	} else
		err = bitmap_init_from_disk(bitmap, start);
		err = bitmap_init_from_disk(bitmap, start);


	if (err)
	if (err)
+5 −0
Original line number Original line Diff line number Diff line
@@ -222,6 +222,10 @@ struct bitmap {
	unsigned long file_pages; /* number of pages in the file */
	unsigned long file_pages; /* number of pages in the file */
	int last_page_size; /* bytes in the last page */
	int last_page_size; /* bytes in the last page */


	unsigned long logattrs; /* used when filemap_attr doesn't exist
				 * because we are working with a dirty_log
				 */

	unsigned long flags;
	unsigned long flags;


	int allclean;
	int allclean;
@@ -243,6 +247,7 @@ struct bitmap {
	wait_queue_head_t behind_wait;
	wait_queue_head_t behind_wait;


	struct sysfs_dirent *sysfs_can_clear;
	struct sysfs_dirent *sysfs_can_clear;

};
};


/* the bitmap API */
/* the bitmap API */
+5 −0
Original line number Original line Diff line number Diff line
@@ -317,6 +317,11 @@ 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.
							 */
							 */
		/* When md is serving under dm, it might use a
		 * dirty_log to store the bits.
		 */
		struct dm_dirty_log *log;

		struct mutex		mutex;
		struct mutex		mutex;
		unsigned long		chunksize;
		unsigned long		chunksize;
		unsigned long		daemon_sleep; /* how many jiffies between updates? */
		unsigned long		daemon_sleep; /* how many jiffies between updates? */