block2mtd.patch
[Posted December 9, 2012 by corbet]
Subject: hacks to block2mtd so it can be used for performance testing.
1/ Fix bug: balance_dirty_pages_ratelimited(mapping) must be called
after set_page_dirty(). This is the only bit that should go to
mainline.
2/ Don't pre-read before writing when writing whole pages. It just
slows us down and the compare will never in my use case help at all.
3/ Add 'erase_map' which is a bitmap of which page-sized block have been
erased. This saves us from having to write lots of 0xff which is much
slower than erase (which ftl does for us anyway).
4/ add head_map which is an array of dirty pages which are the first page in an
erase block. They are not actually written until some other page
in the erase block is written.
UBI always writes the first page immediately after erasing a block, and
then doesn't write the next page for possibly a very long time. As an FTL
has a limited number of open erase block, this will cause each erase block
to be written twice. Be delaying the writeout of the header we only
end up writing each erase block onces.
Signed-off-by: NeilBrown <neilb@suse.de>
--- /home/git/linux/drivers/mtd/devices/block2mtd.c 2012-12-05 15:52:13.969337046 +1100
+++ /usr/src/linux-3.6.6-11/drivers/mtd/devices/block2mtd.c 2012-11-24 19:03:31.515462126 +1100
@@ -28,9 +28,13 @@
struct block_device *blkdev;
struct mtd_info mtd;
struct mutex write_mutex;
+ int ebcnt; /* erase block count */
+ int erase_shift;
+ int map_pages;
+ struct page **head_map;
+ void **erase_map;
};
-
/* Static info about the MTD, used in cleanup_module */
static LIST_HEAD(blkmtd_device_list);
@@ -43,29 +47,18 @@
/* erase a specified part of the device */
static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
{
- struct address_space *mapping = dev->blkdev->bd_inode->i_mapping;
- struct page *page;
int index = to >> PAGE_SHIFT; // page index
int pages = len >> PAGE_SHIFT;
- u_long *p;
- u_long *max;
+ int eb_index = (index & ((dev->mtd.erasesize >> PAGE_SHIFT)-1));
+ if (eb_index == 0 &&
+ dev->head_map[index >> dev->erase_shift]) {
+ page_cache_release(dev->head_map[index >> dev->erase_shift]);
+ dev->head_map[index >> dev->erase_shift] = NULL;
+ }
while (pages) {
- page = page_read(mapping, index);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- max = page_address(page) + PAGE_SIZE;
- for (p=page_address(page); p<max; p++)
- if (*p != -1UL) {
- lock_page(page);
- memset(page_address(page), 0xff, PAGE_SIZE);
- set_page_dirty(page);
- unlock_page(page);
- break;
- }
-
- page_cache_release(page);
+ set_bit(index & ((PAGE_SIZE*8)-1),
+ dev->erase_map[index>>(PAGE_SHIFT+3)]);
pages--;
index++;
}
@@ -109,13 +102,22 @@
cpylen = len; // this page
len = len - cpylen;
- page = page_read(dev->blkdev->bd_inode->i_mapping, index);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- memcpy(buf, page_address(page) + offset, cpylen);
- page_cache_release(page);
+ if (test_bit(index & ((PAGE_SIZE*8)-1),
+ dev->erase_map[index>>(PAGE_SHIFT+3)]))
+ memset(buf, 0xff, cpylen);
+ else if ((index & ((dev->mtd.erasesize >> PAGE_SHIFT)-1)) == 0
+ && dev->head_map[index >> dev->erase_shift]) {
+ page = dev->head_map[index >> dev->erase_shift];
+ memcpy(buf, page_address(page) + offset, cpylen);
+ } else {
+
+ page = page_read(dev->blkdev->bd_inode->i_mapping, index);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+ memcpy(buf, page_address(page) + offset, cpylen);
+ page_cache_release(page);
+ }
if (retlen)
*retlen += cpylen;
buf += cpylen;
@@ -143,17 +145,66 @@
cpylen = len; // this page
len = len - cpylen;
- page = page_read(mapping, index);
- if (IS_ERR(page))
- return PTR_ERR(page);
-
- if (memcmp(page_address(page)+offset, buf, cpylen)) {
+ if ((index & ((dev->mtd.erasesize >> PAGE_SHIFT)-1)) != 0
+ && dev->head_map[index >> dev->erase_shift]) {
+ /* Time to write out the header */
+ page = dev->head_map[index >> dev->erase_shift];
+ dev->head_map[index >> dev->erase_shift] = NULL;
lock_page(page);
+ set_page_dirty(page);
+ unlock_page(page);
+ balance_dirty_pages_ratelimited(mapping);
+ page_cache_release(page);
+ }
+ if ((index & ((dev->mtd.erasesize >> PAGE_SHIFT)-1)) == 0) {
+ page = dev->head_map[index >> dev->erase_shift];
+ if (!page) {
+ if (!test_bit(index & ((PAGE_SIZE*8)-1),
+ dev->erase_map[index>>(PAGE_SHIFT+3)])) {
+ page = page_read(mapping, index);
+ } else {
+ page = find_or_create_page(mapping, index, GFP_KERNEL);
+ memset(page_address(page), 0xff, PAGE_SIZE);
+ dev->head_map[index >> dev->erase_shift] = page;
+ SetPageUptodate(page);
+ unlock_page(page);
+ }
+ }
+ memcpy(page_address(page) + offset, buf, cpylen);
+ } else if ((offset || len < PAGE_SIZE)
+ &&
+ !test_bit(index & ((PAGE_SIZE*8)-1),
+ dev->erase_map[index>>(PAGE_SHIFT+3)])
+ ) {
+ page = page_read(mapping, index);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+
+ if (memcmp(page_address(page)+offset, buf, cpylen)) {
+ lock_page(page);
+ memcpy(page_address(page) + offset, buf, cpylen);
+
+ set_page_dirty(page);
+ unlock_page(page);
+ balance_dirty_pages_ratelimited(mapping);
+ }
+ page_cache_release(page);
+ } else {
+ page = find_or_create_page(mapping, index, GFP_KERNEL);
+ if (IS_ERR(page))
+ return PTR_ERR(page);
+ if (offset || cpylen != PAGE_SIZE)
+ memset(page_address(page), 0xff, PAGE_SIZE);
memcpy(page_address(page) + offset, buf, cpylen);
+ SetPageUptodate(page);
set_page_dirty(page);
unlock_page(page);
+ page_cache_release(page);
+ balance_dirty_pages_ratelimited(mapping);
}
- page_cache_release(page);
+ clear_bit(index & ((PAGE_SIZE*8)-1),
+ dev->erase_map[index>>(PAGE_SHIFT+3)]);
+
if (retlen)
*retlen += cpylen;
@@ -192,9 +243,15 @@
static void block2mtd_free_device(struct block2mtd_dev *dev)
{
+ int i;
+ unsigned long map_pages;
if (!dev)
return;
-
+ map_pages = dev->map_pages;
+ if (dev->erase_map)
+ for (i=0; i < map_pages; i++)
+ kfree(dev->erase_map[i]);
+ kfree(dev->erase_map);
kfree(dev->mtd.name);
if (dev->blkdev) {
@@ -214,6 +271,10 @@
struct block_device *bdev;
struct block2mtd_dev *dev;
char *name;
+ unsigned long map_pages;
+ int i;
+ unsigned long ebcnt;
+ int erase_shift;
if (!devname)
return NULL;
@@ -257,7 +318,7 @@
dev->mtd.name = name;
- dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
+ dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK & ~(unsigned long)(erase_size-1);
dev->mtd.erasesize = erase_size;
dev->mtd.writesize = 1;
dev->mtd.writebufsize = PAGE_SIZE;
@@ -270,6 +331,30 @@
dev->mtd.priv = dev;
dev->mtd.owner = THIS_MODULE;
+ map_pages = dev->mtd.size >> PAGE_SHIFT;
+ map_pages = round_up(map_pages, PAGE_SIZE*8);
+ map_pages >>= PAGE_SHIFT+3;
+ dev->erase_map = kzalloc(map_pages * sizeof(void*), GFP_KERNEL);
+ printk("map_pages = %lu\n", map_pages);
+ for (i = 0; i < map_pages; i++)
+ dev->erase_map[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ dev->map_pages = map_pages;
+
+ ebcnt = dev->mtd.size;
+ erase_shift = 0;
+ while (erase_size > 1) {
+ ebcnt >>= 1;
+ erase_size >>= 1;
+ erase_shift++;
+ }
+ dev->ebcnt = ebcnt;
+ dev->erase_shift = erase_shift - PAGE_SHIFT;
+ dev->head_map = kzalloc(dev->ebcnt * sizeof(struct page*),
+ GFP_KERNEL);
+
+ printk("ebcnt=%lu erase_shift=%d map=%p\n",
+ ebcnt, erase_shift, dev->head_map);
+
if (mtd_device_register(&dev->mtd, NULL, 0)) {
/* Device didn't get added, so free the entry */
goto devinit_err;
(
Log in to post comments)