rewrite rd
Subsystems (5)
PATCH 1/1 rewrite rd
Hi,
This is my proposal for a (hopefully) backwards compatible rd driver. The motivation for me is not pressing, except that I have this code sitting here that is either going to rot or get merged. I'm happy to make myself maintainer of this code, but if any real block device driver writer would like to, that would be fine by me ;)
Comments?
--
This is a rewrite of the ramdisk block device driver.
The old one is really difficult because it effectively implements a block device which serves data out of its own buffer cache. It relies on the dirty bit being set, to pin its backing store in cache, however there are non trivial paths which can clear the dirty bit (eg. try_to_free_buffers()), which had recently lead to data corruption. And in general it is completely wrong for a block device driver to do this.
The new one is more like a regular block device driver. It has no idea about vm/vfs stuff. It's backing store is similar to the buffer cache (a simple radix-tree of pages), but it doesn't know anything about page cache (the pages in the radix tree are not pagecache pages).
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
The fact that it now goes through all the regular vm/fs paths makes it much more useful for testing, too.
text data bss dec hex filename 2837 849 384 4070 fe6 drivers/block/rd.o 3528 371 12 3911 f47 drivers/block/brd.o
Text is larger, but data and bss are smaller, making total size smaller.
A few other nice things about it:
- Similar structure and layout to the new loop device handlinag.
- Dynamic ramdisk creation.
- Runtime flexible buffer head size (because it is no longer part of the ramdisk code).
- Boot / load time flexible ramdisk size, which could easily be extended to a per-ramdisk runtime changeable size (eg. with an ioctl).
--- MAINTAINERS | 5 drivers/block/Kconfig | 12 - drivers/block/Makefile | 2 drivers/block/brd.c | 500 +++++++++++++++++++++++++++++++++++++++++++++ drivers/block/rd.c | 536 ------------------------------------------------- fs/buffer.c | 1 6 files changed, 508 insertions(+), 548 deletions(-)
drivers/block/Kconfig +1 -11
@@ -311,7 +311,7 @@ config BLK_DEV_UB If unsure, say N. config BLK_DEV_RAM - tristate "RAM disk support" + tristate "RAM block device support" ---help--- Saying Y here will allow you to use a portion of your RAM memory as a block device, so that you can make file systems on it, read and
@@ -346,16 +346,6 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. -config BLK_DEV_RAM_BLOCKSIZE - int "Default RAM disk block size (bytes)" - depends on BLK_DEV_RAM - default "1024" - help - The default value is 1024 bytes. PAGE_SIZE is a much more - efficient choice however. The default is kept to ensure initrd - setups function - apparently needed by the rd_load_image routine - that supposes the filesystem in the image uses a 1024 blocksize. - config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media" depends on !UML
drivers/block/Makefile +1 -1
@@ -11,7 +11,7 @@ obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o obj-$(CONFIG_PS3_DISK) += ps3disk.o obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o -obj-$(CONFIG_BLK_DEV_RAM) += rd.o +obj-$(CONFIG_BLK_DEV_RAM) += brd.o obj-$(CONFIG_BLK_DEV_LOOP) += loop.o obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o
drivers/block/brd.c new file +536 -0
536 lines not on this page · raw message
drivers/block/rd.c deleted +0 -536
536 lines not on this page · raw message
MAINTAINERS +5 -0
@@ -3163,6 +3163,11 @@ W: http://rt2x00.serialmonkey.com/ S: Maintained F: drivers/net/wireless/rt2x00/ +RAMDISK RAM BLOCK DEVICE DRIVER +P: Nick Piggin +M: [email protected] +S: Maintained + RANDOM NUMBER DRIVER P: Matt Mackall M: [email protected]
fs/buffer.c +1 -0
@@ -1436,6 +1436,7 @@ void invalidate_bh_lrus(void) {on_each_cpu (invalidate_bh_lru,NULL ,1 ,1 ); } +EXPORT_SYMBOL_GPL (invalidate_bh_lrus);void set_bh_page (struct buffer_head *bh,struct page *page,unsigned long offset)
Andrew Morton
maintainerQuoted 26 lines · On Tue, 4 Dec 2007 05:26:28 +0100 Nick Piggin <[email protected]> wrote:
Hi,
This is my proposal for a (hopefully) backwards compatible rd driver. The motivation for me is not pressing, except that I have this code sitting here that is either going to rot or get merged. I'm happy to make myself maintainer of this code, but if any real block device driver writer would like to, that would be fine by me ;)
Comments?
--
This is a rewrite of the ramdisk block device driver.
The old one is really difficult because it effectively implements a block device which serves data out of its own buffer cache. It relies on the dirty bit being set, to pin its backing store in cache, however there are non trivial paths which can clear the dirty bit (eg.
try_to_free_buffers()), which had recently lead to data corruption. And in general it is completely wrong for a block device driver to do this.The new one is more like a regular block device driver. It has no idea about vm/vfs stuff. It's backing store is similar to the buffer cache (a simple radix-tree of pages), but it doesn't know anything about page cache (the pages in the radix tree are not pagecache pages).
Quoted 7 lines
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
what about mmap(/dev/ram0)?
Quoted 9 lines
The fact that it now goes through all the regular vm/fs paths makes it much more useful for testing, too.
text data bss dec hex filename 2837 849 384 4070 fe6 drivers/block/rd.o 3528 371 12 3911 f47 drivers/block/brd.oText is larger, but data and bss are smaller, making total size smaller.
Quoted 7 lines
A few other nice things about it:
- Similar structure and layout to the new loop device handlinag.
- Dynamic ramdisk creation.
- Runtime flexible buffer head size (because it is no longer part of the ramdisk code).
- Boot / load time flexible ramdisk size, which could easily be extended to a per-ramdisk runtime changeable size (eg. with an ioctl).
This ramdisk driver can use highmem whereas the existing one can't (yes?). That's a pretty major difference. Plus look at the revoltingness in rd.c's mapping_set_gfp_mask()s.
Quoted 16 lines
+++
linux-2.6/drivers/block/brd.cdrivers/block/brd.c new file +14
-0@@ -0,0 +1,536 @@ +/* +* Ram backed block device driver. +* +* Copyright (C) 2007 Nick Piggin +* Copyright (C) 2007 Novell Inc. +* +* Parts derived from drivers/block/rd.c, and drivers/block/loop.c, copyright +* of their respective owners. +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/major.h>
drivers/block/brd.c line 24
Quoted 10 lines
drivers/block/brd.c new file +10
-0+#include <linux/blkdev.h> +#include <linux/bio.h> +#include <linux/highmem.h> +#include <linux/gfp.h> +#include <linux/radix-tree.h> +#include <linux/buffer_head.h> /* invalidate_bh_lrus() */ + +#include <asm/uaccess.h> + +#define SECTOR_SHIFT 9
That's our third definition of SECTOR_SHIFT.
Quoted 25 lines
drivers/block/brd.c new file +25
-0+#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT) + +/* +* Each block ramdisk device has a radix_tree brd_pages of pages that stores +* the pages containing the block device's contents. A brd page's ->index is +* its offset in PAGE_SIZE units. This is similar to, but in no way connected +* with, the kernel's pagecache or buffer cache (which sit above our block +* device). +*/ +struct brd_device { +int brd_number; +int brd_refcnt; +loff_t brd_offset; +loff_t brd_sizelimit; +unsigned brd_blocksize; + +struct request_queue *brd_queue; +struct gendisk *brd_disk; +struct list_head brd_list; + +/* +* Backing store of pages and lock to protect it. This is the contents +* of the block device. +*/
drivers/block/brd.c line 59
Quoted 10 lines
drivers/block/brd.c new file +10
-0+spinlock_t brd_lock; +struct radix_tree_root brd_pages; +}; + +/* +* Look up and return a brd's page for a given sector. +*/ +static struct page *brd_lookup_page (struct brd_device *brd,sector_t sector) +{ +unsigned long idx;
Could use pgoff_t here if you think that's clearer.
Quoted 28 lines
drivers/block/brd.c new file +28
-0+struct page *page; + +/* +* The page lifetime is protected by the fact that we have opened the +* device node -- brd pages will never be deleted under us, so we +* don't need any further locking or refcounting. +* +* This is strictly true for the radix-tree nodes as well (ie. we +* don't actually need the rcu_read_lock()), however that is not a +* documented feature of the radix-tree API so it is better to be +* safe here (we don't have total exclusion from radix tree updates +* here, only deletes). +*/ +rcu_read_lock (); + idx = sector >>PAGE_SECTORS_SHIFT ;/* sector to page index */ + page =radix_tree_lookup (&brd->brd_pages, idx); +rcu_read_unlock (); + +BUG_ON (page && page->index != idx); + +return page; +} + +/* +* Look up and return a brd's page for a given sector. +* If one does not exist, allocate an empty page, and insert that. Then +* return it. +*/
drivers/block/brd.c line 97
Quoted 10 lines
drivers/block/brd.c new file +10
-0+static struct page *brd_insert_page (struct brd_device *brd,sector_t sector) +{ +unsigned long idx; +struct page *page; + + page =brd_lookup_page (brd, sector); +if (page) +return page; + + page =alloc_page (GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);
Why GFP_NOIO?
Have you thought about __GFP_MOVABLE treatment here? Seems pretty desirable as this sucker can be large.
Quoted 45 lines
drivers/block/brd.c new file +45
-0+if (!page) +return NULL ; + +if (radix_tree_preload (GFP_NOIO )) { +__free_page (page); +return NULL ; + } + +spin_lock (&brd->brd_lock); + idx = sector >>PAGE_SECTORS_SHIFT ; +if (radix_tree_insert (&brd->brd_pages, idx, page)) { +__free_page (page); + page =radix_tree_lookup (&brd->brd_pages, idx); +BUG_ON (!page); +BUG_ON (page->index != idx); + }else + page->index = idx; +spin_unlock (&brd->brd_lock); + +radix_tree_preload_end (); + +return page; +} + +/* +* Free all backing store pages and radix tree. This must only be called when +* there are no other users of the device. +*/ +#define FREE_BATCH 16 +static void brd_free_pages (struct brd_device *brd) +{ +unsigned long pos =0 ; +struct page *pages[FREE_BATCH ]; +int nr_pages; + + do { + int i; + + nr_pages = radix_tree_gang_lookup(&brd->brd_pages, + (void **)pages, pos, FREE_BATCH); + + for (i = 0; i < nr_pages; i++) { + void *ret; + + BUG_ON(pages[i]->index < pos);
drivers/block/brd.c line 152
Quoted 10 lines
drivers/block/brd.c new file +10
-0+ pos = pages[i]->index; + ret =radix_tree_delete (&brd->brd_pages, pos); +BUG_ON (!ret || ret != pages[i]); +__free_page (pages[i]); + } + + pos++; + + }while (nr_pages ==FREE_BATCH ); +}
I have vague memories that radix_tree_gang_lookup()'s "naive" implementation may return fewer items than you asked for even when there are more items remaining - when it hits certain boundaries.
drivers/block/brd.c line 162
Quoted 9 lines
drivers/block/brd.c new file +9
-0+/* +* copy_to_brd_setup must be called before copy_to_brd. It may sleep. +*/ +static int copy_to_brd_setup (struct brd_device *brd,sector_t sector,size_t n) +{ +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset);
use min_t. Or, better, sort out the types.
Quoted 12 lines
drivers/block/brd.c new file +12
-0+if (!brd_insert_page (brd, sector)) +return -ENOMEM ; +if (copy < n) { + sector += copy >>SECTOR_SHIFT ; +if (!brd_insert_page (brd, sector)) +return -ENOMEM ; + } +return 0 ; +} + +/* +* Copy n bytes from src to the brd starting at sector. Does not sleep.
drivers/block/brd.c line 184
Quoted 10 lines
drivers/block/brd.c new file +10
-0+*/ +static void copy_to_brd (struct brd_device *brd,const void *src, +sector_t sector,size_t n) +{ +struct page *page; +void *dst; +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset);
Ditto.
drivers/block/brd.c line 190
Quoted 6 lines
drivers/block/brd.c new file +6
-0+ page =brd_lookup_page (brd, sector); +BUG_ON (!page); + + dst =kmap_atomic (page,KM_USER1 ); +memcpy (dst + offset, src, copy); +kunmap_atomic (dst,KM_USER1 );
Might need flush_dcache_page() if mmap gets sorted out.
Quoted 15 lines
drivers/block/brd.c new file +15
-0+if (copy < n) { + src += copy; + sector += copy >>SECTOR_SHIFT ; + copy = n - copy; + page =brd_lookup_page (brd, sector); +BUG_ON (!page); + + dst =kmap_atomic (page,KM_USER1 ); +memcpy (dst, src, copy); +kunmap_atomic (dst,KM_USER1 ); + } +} + +/* +* Copy n bytes to dst from the brd starting at sector. Does not sleep.
drivers/block/brd.c line 216
Quoted 10 lines
drivers/block/brd.c new file +10
-0+*/ +static void copy_from_brd (void *dst,struct brd_device *brd, +sector_t sector,size_t n) +{ +struct page *page; +const void *src; +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset);
tritto.
Quoted 32 lines
drivers/block/brd.c new file +32
-0+ page =brd_lookup_page (brd, sector); +if (page) { + src =kmap_atomic (page,KM_USER1 ); +memcpy (dst, src + offset, copy); +kunmap_atomic (src,KM_USER1 ); + }else +memset (dst,0 , copy); + +if (copy < n) { + dst += copy; + sector += copy >>SECTOR_SHIFT ; + copy = n - copy; + page =brd_lookup_page (brd, sector); +if (page) { + src =kmap_atomic (page,KM_USER1 ); +memcpy (dst, src, copy); +kunmap_atomic (src,KM_USER1 ); + }else +memset (dst,0 , copy); + } +} + +/* +* Process a single bvec of a bio. +*/ +static int brd_do_bvec (struct brd_device *brd,struct page *page, +unsigned int len,unsigned int off,int rw, +sector_t sector) +{ +void *mem; +int err =0 ; +
drivers/block/brd.c line 258
Quoted 10 lines
drivers/block/brd.c new file +10
-0+if (rw !=READ ) { + err =copy_to_brd_setup (brd, sector, len); +if (err) + goto out; + } + + mem =kmap_atomic (page,KM_USER0 ); +if (rw ==READ ) { +copy_from_brd (mem + off, brd, sector, len); +flush_dcache_page (page);
hm, there's a flush_dcache_page(). I guess you've throught it through ;)
Quoted 24 lines
drivers/block/brd.c new file +21
-0+ }else +copy_to_brd (brd, mem + off, sector, len); +kunmap_atomic (mem,KM_USER0 ); + +out: +return err; +} + +static int brd_ioctl (struct inode *inode,struct file *file, +unsigned int cmd,unsigned long arg) +{ +int error; +struct block_device *bdev = inode->i_bdev; +struct brd_device *brd = bdev->bd_disk->private_data; + +if (cmd !=BLKFLSBUF ) +return -ENOTTY ; + +/* +* ram device BLKFLSBUF has special semantics, we want to actually +* release and destroy the ramdisk data.
drivers/block/brd.c line 323
Quoted 10 lines
drivers/block/brd.c new file +10
-0+*/ +mutex_lock (&bdev->bd_mutex); + error = -EBUSY ; +if (bdev->bd_openers <=1 ) { +/* +* Invalidate the cache first, so it isn't written +* back to the device. +*/ +invalidate_bh_lrus (); +truncate_inode_pages (bdev->bd_inode->i_mapping,0 );
hm, some other thread can instantiate pagecache here. I guess it's always been like that and there's not a lot we can (or should) do about it.
Quoted 14 lines
drivers/block/brd.c new file +8
-0+brd_free_pages (brd); + error =0 ; + } +mutex_unlock (&bdev->bd_mutex); + +return error; +} +
fs/buffer.c void invalidate_bh_lrus(void) · line 1439
Quoted 4 lines
fs/buffer.c +1
-0@@ -1436,6 +1436,7 @@ void invalidate_bh_lrus(void) {on_each_cpu (invalidate_bh_lru,NULL ,1 ,1 ); } +EXPORT_SYMBOL_GPL (invalidate_bh_lrus);
Maybe create a new helper function which does invalidate_bh_lrus()+truncate_inode_pages(), call that from kill_bdev() and here, make invalidate_bh_lrus() static.
That's a separate patch, I guess.
Quoted 11 lines · Andrew Morton
Quoted 8 lines · On Tue, 4 Dec 2007 05:26:28 +0100 Nick Piggin <[email protected]> wrote:
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
what about mmap(/dev/ram0)?
Same thing I guess -- it will use more memory in the common case, but should actually be able to reclaim slightly more memory under pressure, so the tiny systems guys shouldn't have too much trouble.
And we're avoiding the whole class of aliasing problems where mmap on the old rd driver means that you're creating new mappings to your backing store pages.
Quoted 10 lines
Quoted 9 lines
Text is larger, but data and bss are smaller, making total size smaller.
A few other nice things about it:
- Similar structure and layout to the new loop device handlinag.
- Dynamic ramdisk creation.
- Runtime flexible buffer head size (because it is no longer part of the ramdisk code).
- Boot / load time flexible ramdisk size, which could easily be extended to a per-ramdisk runtime changeable size (eg. with an ioctl).
Quoted 3 lines
This ramdisk driver can use highmem whereas the existing one can't (yes?). That's a pretty major difference. Plus look at the revoltingness in rd.c's mapping_set_gfp_mask()s.
Ah yep, there is the highmem advantage too.
Quoted 3 lines
Quoted 1 line
+#define
SECTOR_SHIFT9That's our third definition of
SECTOR_SHIFT.
Or 7th, depend on how you count ;) I always thought redefining it is a prerequsite to getting anything merged into the block layer, so I'm too scared to put it in include/linux/blkdev.h ;)
Quoted 8 lines
drivers/block/brd.c line 59
Quoted 6 lines
drivers/block/brd.c new file +6
-0+/* +* Look up and return a brd's page for a given sector. +*/ +static struct page *brd_lookup_page (struct brd_device *brd,sector_t sector) +{ +unsigned long idx;Could use
pgoff_there if you think that's clearer.
I guess it is. Although on one hand the radix-tree uses unsigned long, but on the other hand, page->index is pgoff.
Quoted 11 lines
drivers/block/brd.c line 97
Quoted 9 lines
drivers/block/brd.c new file +9
-0+{ +unsigned long idx; +struct page *page; + + page =brd_lookup_page (brd, sector); +if (page) +return page; + + page =alloc_page (GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);Why
GFP_NOIO?
I guess it has similar issues to rd.c -- we can't risk recursing into the block layer here. However unlike rd.c, we _could_ easily add some mode or ioctl to allocate the backing store upfront, with full reclaim flags...
Quoted 2 lines
Have you thought about
__GFP_MOVABLEtreatment here? Seems pretty desirable as this sucker can be large.
AFAIK that only applies to pagecache but I haven't been paying much attention to that stuff lately. It wouldn't be hard to move these pages around, if we had a hook from the vm for it.
Quoted 22 lines
Quoted 22 lines
drivers/block/brd.c new file +22
-0+static void brd_free_pages (struct brd_device *brd) +{ +unsigned long pos =0 ; +struct page *pages[FREE_BATCH ]; +int nr_pages; + + do { + int i; + + nr_pages = radix_tree_gang_lookup(&brd->brd_pages, + (void **)pages, pos, FREE_BATCH); + + for (i = 0; i < nr_pages; i++) { + void *ret; + + BUG_ON(pages[i]->index < pos); + pos = pages[i]->index; + ret = radix_tree_delete(&brd->brd_pages, pos); + BUG_ON(!ret || ret != pages[i]); + __free_page(pages[i]); + } +
Quoted 8 lines
drivers/block/brd.c line 152
Quoted 4 lines
drivers/block/brd.c new file +4
-0+ pos++; + + }while (nr_pages ==FREE_BATCH ); +}I have vague memories that radix_tree_gang_lookup()'s "naive" implementation may return fewer items than you asked for even when there are more items remaining - when it hits certain boundaries.
Good memory, but it's the low level leaf traversal that bales out at boundaries. The higher level code then retries, so we should be OK here.
Quoted 11 lines
drivers/block/brd.c line 162
Quoted 9 lines
drivers/block/brd.c new file +9
-0+/* +* copy_to_brd_setup must be called before copy_to_brd. It may sleep. +*/ +static int copy_to_brd_setup (struct brd_device *brd,sector_t sector,size_t n) +{ +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset);use
min_t. Or, better, sort out the types.
I guess the API is using size_t, so that would be the more approprite type to convert to. And I'll use min_t too.
Quoted 12 lines
drivers/block/brd.c line 184
Quoted 9 lines
drivers/block/brd.c new file +9
-0+static void copy_to_brd (struct brd_device *brd,const void *src, +sector_t sector,size_t n) +{ +struct page *page; +void *dst; +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset);Ditto.
Quoted 8 lines
drivers/block/brd.c line 190
Quoted 6 lines
drivers/block/brd.c new file +6
-0+ page =brd_lookup_page (brd, sector); +BUG_ON (!page); + + dst =kmap_atomic (page,KM_USER1 ); +memcpy (dst + offset, src, copy); +kunmap_atomic (dst,KM_USER1 );Might need
flush_dcache_page()if mmap gets sorted out.
The brd backing store never gets any userspace aliases, so I think it should be OK when copying into it.
Quoted 11 lines
Quoted 11 lines
drivers/block/brd.c new file +11
-0+static int brd_do_bvec (struct brd_device *brd,struct page *page, +unsigned int len,unsigned int off,int rw, +sector_t sector) +{ +void *mem; +int err =0 ; + +if (rw !=READ ) { + err =copy_to_brd_setup (brd, sector, len); +if (err) + goto out;
Quoted 8 lines
drivers/block/brd.c line 258
Quoted 6 lines
drivers/block/brd.c new file +6
-0+ } + + mem =kmap_atomic (page,KM_USER0 ); +if (rw ==READ ) { +copy_from_brd (mem + off, brd, sector, len); +flush_dcache_page (page);hm, there's a
flush_dcache_page(). I guess you've throught it through ;)
Copying into the pagecache yes needs to flush I think. Although strictly it also needs a write barrier to prevent these stores being passed by the store to set PG_uptodate (but that's another story, which I think should be fixed in the PageUptodate macros rather than device drivers and filesystems...)
Quoted 4 lines
Quoted 4 lines
drivers/block/brd.c new file +4
-0+mutex_lock (&bdev->bd_mutex); + error = -EBUSY ; +if (bdev->bd_openers <=1 ) { +/*
Quoted 8 lines
drivers/block/brd.c line 323
Quoted 5 lines
drivers/block/brd.c new file +5
-0+* Invalidate the cache first, so it isn't written +* back to the device. +*/ +invalidate_bh_lrus (); +truncate_inode_pages (bdev->bd_inode->i_mapping,0 );hm, some other thread can instantiate pagecache here. I guess it's always been like that and there's not a lot we can (or should) do about it.
Yeah, another thread could do that...
Quoted 23 lines
Quoted 14 lines
drivers/block/brd.c new file +8
-0+brd_free_pages (brd); + error =0 ; + } +mutex_unlock (&bdev->bd_mutex); + +return error; +} +fs/buffer.c void invalidate_bh_lrus(void) · line 1439
Quoted 4 lines
fs/buffer.c +1
-0@@ -1436,6 +1436,7 @@ void invalidate_bh_lrus(void) {on_each_cpu (invalidate_bh_lru,NULL ,1 ,1 ); } +EXPORT_SYMBOL_GPL (invalidate_bh_lrus);Maybe create a new helper function which does invalidate_bh_lrus()+truncate_inode_pages(), call that from
kill_bdev()and here, makeinvalidate_bh_lrus()static.
Quoted 1 line
That's a separate patch, I guess.
I was thinking also that perhaps the buffer cache layer could intercept the ioctl on the way through and flush the buffer cache before going to the device -- so device drivers don't have to have _any_ knowledge about the buffer cache...?
Thanks for the review, I'll post an incremental patch in a sec.
Quoted 1 line · Nick Piggin
Thanks for the review, I'll post an incremental patch in a sec.
drivers/block/brd.c +17 -5
@@ -56,7 +56,7 @@ struct brd_device {*/ static struct page *brd_lookup_page (struct brd_device *brd,sector_t sector) { -unsigned long idx; +pgoff_t idx;struct page *page;/*
@@ -87,13 +87,17 @@ static struct page *brd_lookup_page(stru*/ static struct page *brd_insert_page (struct brd_device *brd,sector_t sector) { -unsigned long idx; +pgoff_t idx;struct page *page; page =brd_lookup_page (brd, sector);if (page)return page; +/* +* Must use NOIO because we don't want to recurse back into the +* block or filesystem layers from page reclaim. +*/ page =alloc_page (GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);if (!page)return NULL ;
@@ -148,6 +152,11 @@ static void brd_free_pages(struct brd_de pos++; +/* +* This assumes radix_tree_gang_lookup always returns as +* many pages as possible. If the radix-tree code changes, +* so will this have to. +*/ }while (nr_pages ==FREE_BATCH ); }
@@ -159,7 +168,7 @@ static int copy_to_brd_setup(struct brd_unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ;size_t copy; - copy =min ((unsigned long )n,PAGE_SIZE - offset); + copy =min_t (size_t, n,PAGE_SIZE - offset);if (!brd_insert_page (brd, sector))return -ENOMEM ;if (copy < n) {
@@ -181,7 +190,7 @@ static void copy_to_brd(struct brd_devicunsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ;size_t copy; - copy =min ((unsigned long )n,PAGE_SIZE - offset); + copy =min_t (size_t, n,PAGE_SIZE - offset); page =brd_lookup_page (brd, sector);BUG_ON (!page);
@@ -213,7 +222,7 @@ static void copy_from_brd(void *dst, strunsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ;size_t copy; - copy =min ((unsigned long )n,PAGE_SIZE - offset); + copy =min_t (size_t, n,PAGE_SIZE - offset); page =brd_lookup_page (brd, sector);if (page) { src =kmap_atomic (page,KM_USER1 );
@@ -318,6 +327,9 @@ static int brd_ioctl(struct inode *inode/* * Invalidate the cache first, so it isn't written * back to the device. +* +* Another thread might instantiate more buffercache here, +* but there is not much we can do to close that race. */ invalidate_bh_lrus ();truncate_inode_pages (bdev->bd_inode->i_mapping,0 );
Quoted 7 lines · On Monday 03 December 2007 22:26:28 Nick Piggin wrote:
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
For the embedded world, initramfs has pretty much rendered initrd obsolete, and that was the biggest user of the ramdisk code I know of. Beyond that, loopback mounts give you more flexible transient block devices than ramdisks do. (In fact, ramdisks are such an amazing pain to use/size/free that if I really needed something like that I'd just make a loopback mount in a ramfs instance.)
Embedded users who still want a block interface for memory are generally trying to use a cramfs or squashfs image out of ROM or flash, although there are flash-specific filesystems for this and I dunno if they're actually mounting /dev/mem at an offset or something (md? losetup -o? Beats me, I haven't tried that myself yet...)
Rob
Signature · 2 lines
"One of my most productive days was throwing away 1000 lines of code." - Ken Thompson.
Quoted 9 lines · Rob Landley
Quoted 7 lines · On Monday 03 December 2007 22:26:28 Nick Piggin wrote:
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
Quoted 6 lines
For the embedded world, initramfs has pretty much rendered initrd obsolete, and that was the biggest user of the ramdisk code I know of. Beyond that, loopback mounts give you more flexible transient block devices than ramdisks do. (In fact, ramdisks are such an amazing pain to use/size/free that if I really needed something like that I'd just make a loopback mount in a ramfs instance.)
They are, although we could easily hook up a couple more ioctls for them now (if anybody is interested).
The rd driver can potentially be a _lot_ more scalable than the loop driver. It's completely lockless in the fastpath and doesn't even dirty any cachelines except for the actual destination memory. OK, this is only really important for testing purposes (eg. testing scalability of a filesystem etc.) But it is one reason why I (personally) want brd...
Quoted 5 lines
Embedded users who still want a block interface for memory are generally trying to use a cramfs or squashfs image out of ROM or flash, although there are flash-specific filesystems for this and I dunno if they're actually mounting
/dev/memat an offset or something (md? losetup -o? Beats me, I haven't tried that myself yet...)
OK, it would be interesting to hear from anyone using rd for things like cramfs. I don't think we can get rid of the code entirely, but it sounds like the few downsides of the new code won't be big problems.
Thanks for the feedback.
Quoted 17 lines · On Tuesday 04 December 2007 03:29:40 Nick Piggin wrote:
Quoted 9 lines · Rob Landley
Quoted 7 lines · On Monday 03 December 2007 22:26:28 Nick Piggin wrote:
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
Quoted 6 lines
For the embedded world, initramfs has pretty much rendered initrd obsolete, and that was the biggest user of the ramdisk code I know of. Beyond that, loopback mounts give you more flexible transient block devices than ramdisks do. (In fact, ramdisks are such an amazing pain to use/size/free that if I really needed something like that I'd just make a loopback mount in a ramfs instance.)
Quoted 4 lines
They are, although we could easily hook up a couple more ioctls for them now (if anybody is interested).
From a usability perspective, an ioctl is no substitute for being able to
resize a device with "dd". (Or for that matter, make it sparse.)
Quoted 5 lines
The rd driver can potentially be a _lot_ more scalable than the loop driver. It's completely lockless in the fastpath and doesn't even dirty any cachelines except for the actual destination memory. OK, this is only really important for testing purposes (eg. testing scalability of a filesystem etc.) But it is one reason why I (personally) want brd...
I wouldn't say not important: The "database in RAM" people will probably love you, at least if they insist on using Oracle. (Filesystem schmilesystem, gimme a raw block device and let me implement my own filesystem from userspace...)
But I'm not personally likely to care. :)
Quoted 8 lines
Quoted 5 lines
Embedded users who still want a block interface for memory are generally trying to use a cramfs or squashfs image out of ROM or flash, although there are flash-specific filesystems for this and I dunno if they're actually mounting
/dev/memat an offset or something (md? losetup -o? Beats me, I haven't tried that myself yet...)OK, it would be interesting to hear from anyone using rd for things like cramfs.
I'd be interested in that too. I've heard it proposed a lot but not actually seen anyone bother to implement it yet...
Rob
Signature · 2 lines
"One of my most productive days was throwing away 1000 lines of code." - Ken Thompson.
Am Dienstag, 4. Dezember 2007 schrieb Nick Piggin:
[...]
Quoted 7 lines
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
This is just an idea, I dont know if it is worth the trouble, but have you though about implementing direct_access for brd? That would allow execute-in-place (xip) on brd eliminating the extra copy.
Christian
Quoted 10 lines · Christian Borntraeger
Am Dienstag, 4. Dezember 2007 schrieb Nick Piggin:
[...]Quoted 7 lines
There is one slight downside -- direct block device access and filesystem metadata access goes through an extra copy and gets stored in RAM twice. However, this downside is only slight, because the real buffercache of the device is now reclaimable (because we're not playing crazy games with it), so under memory intensive situations, footprint should effectively be the same -- maybe even a slight advantage to the new driver because it can also reclaim buffer heads.
Quoted 3 lines
This is just an idea, I dont know if it is worth the trouble, but have you though about implementing
direct_accessfor brd? That would allow execute-in-place (xip) on brd eliminating the extra copy.
Actually that's a pretty good idea. It would allow xip to be tested without special hardware as well...
I'll see what the patch looks like. Thanks
PATCH 1/1 rd: support XIP
Quoted 9 lines · Nick Piggin
Quoted 4 lines
This is just an idea, I dont know if it is worth the trouble, but have you though about implementing
direct_accessfor brd? That would allow execute-in-place (xip) on brd eliminating the extra copy.Actually that's a pretty good idea. It would allow xip to be tested without special hardware as well...
I'll see what the patch looks like. Thanks
This seems to work, although I needed another patch for ext2 too. Never run XIP before, but I'm able to execute files out of the -oxip mount, so I'm guessing it's working ;)
---
Support direct_access XIP method with brd.
---
drivers/block/Kconfig +10 -0
@@ -346,6 +346,16 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. +config BLK_DEV_XIP + bool "Support XIP filesystems on RAM block device" + depends on BLK_DEV_RAM + default n + help + Support XIP filesystems (such as ext2 with XIP support on) on + top of block ram device. This will slightly enlarge the kernel, and + will prevent RAM block device backing store memory from being + allocated from highmem (only a problem for highmem systems). + config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media" depends on !UML
drivers/block/brd.c +37 -2
@@ -89,6 +89,7 @@ static struct page *brd_insert_page(stru {pgoff_t idx;struct page *page; +gfp_t gfp_flags; page =brd_lookup_page (brd, sector);if (page)
@@ -97,7 +98,16 @@ static struct page *brd_insert_page(stru/* * Must use NOIO because we don't want to recurse back into the * block or filesystem layers from page reclaim. +* +* Cannot support XIP and highmem, because our ->direct_access +* routine for XIP must return memory that is always addressable. +* If XIP was reworked to use pfns and kmap throughout, this +* restriction might be able to be lifted. */ + gfp_flags =GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endif page =alloc_page (GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);if (!page)return NULL ;
@@ -307,6 +317,28 @@ out:return 0 ; } +#ifdef CONFIG_BLK_DEV_XIP +static int brd_direct_access (struct block_device *bdev,sector_t sector, +unsigned long *data) +{ +struct brd_device *brd = bdev->bd_disk->private_data; +struct page *page; + +if (!brd) +return -ENODEV ; +if (sector & (PAGE_SECTORS -1 )) +return -EINVAL ; +if (sector +PAGE_SECTORS >get_capacity (bdev->bd_disk)) +return -ERANGE ; + page =brd_insert_page (brd, sector); +if (!page) +return -ENOMEM ; + *data = (unsigned long )page_address (page); + +return 0 ; +} +#endif +static int brd_ioctl (struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) {
@@ -342,8 +374,11 @@ static int brd_ioctl(struct inode *inode }static struct block_device_operations brd_fops = { - .owner =THIS_MODULE , - .ioctl = brd_ioctl, + .owner =THIS_MODULE , + .ioctl = brd_ioctl, +#ifdef CONFIG_BLK_DEV_XIP + .direct_access = brd_direct_access, +#endif };/*
PATCH 1/1 ext2: xip check fix
Am I missing something here? I wonder how s390 works without this change?
--
ext2 should not worry about checking sb->s_blocksize for XIP before the sb's blocksize actually gets set.
---
fs/ext2/super.c +1 -2
@@ -844,8 +844,7 @@ static int ext2_fill_super(struct super_ blocksize =BLOCK_SIZE <<le32_to_cpu (sbi->s_es->s_log_block_size); -if ((ext2_use_xip (sb)) && ((blocksize !=PAGE_SIZE ) || - (sb->s_blocksize != blocksize))) { +if (ext2_use_xip (sb) && blocksize !=PAGE_SIZE ) {if (!silent)printk ("XIP: Unsupported blocksize\n" ); goto failed_mount;
Re: [patch] ext2: xip check fix
Quoted 13 lines · Nick Piggin
Am I missing something here? I wonder how s390 works without this change?
--
ext2 should not worry about checkingsb->s_blocksizefor XIP before the sb's blocksize actually gets set.Signed-off-by Nick Piggin <[email protected]>---
Index:linux-2.6/fs/ext2/super.c
===================================================================
fs/ext2/super.c static int ext2_fill_super(struct super_ · line 850
Quoted 9 lines
fs/ext2/super.c +1
-2@@ -844,8 +844,7 @@ static int ext2_fill_super(struct super_ blocksize =BLOCK_SIZE <<le32_to_cpu (sbi->s_es->s_log_block_size); -if ((ext2_use_xip (sb)) && ((blocksize !=PAGE_SIZE ) || - (sb->s_blocksize != blocksize))) { +if (ext2_use_xip (sb) && blocksize !=PAGE_SIZE ) {if (!silent)printk ("XIP: Unsupported blocksize\n" ); goto failed_mount;
"blocksize" contains the blocksize of the device here, and sb->s_blocksize does contain the filesystem block size as saved in the super block. Xip does only work, if both do match PAGE_SIZE because it does'nt support multiple calls to direct_access in the get_xip_page address space operation. Thus we check both here, actually this was changed from how it looks after your patch as a bugfix where our tester tried a 4k filesystem on a 2k blockdev. Did I miss something?
Re: [patch] ext2: xip check fix
Quoted 17 lines · Carsten Otte
Quoted 16 lines · Nick Piggin
Am I missing something here? I wonder how s390 works without this change?
--
ext2 should not worry about checkingsb->s_blocksizefor XIP before the sb's blocksize actually gets set.Signed-off-by Nick Piggin <[email protected]>---
Index:linux-2.6/fs/ext2/super.c
===================================================================fs/ext2/super.c +0
-0@@ -844,8 +844,7 @@ static int ext2_fill_super(struct super_ blocksize =BLOCK_SIZE <<le32_to_cpu (sbi->s_es->s_log_block_size);
Quoted 9 lines
fs/ext2/super.c static int ext2_fill_super(struct super_ · line 850
Quoted 6 lines
fs/ext2/super.c +1
-2-if ((ext2_use_xip (sb)) && ((blocksize !=PAGE_SIZE ) || - (sb->s_blocksize != blocksize))) { +if (ext2_use_xip (sb) && blocksize !=PAGE_SIZE ) {if (!silent)printk ("XIP: Unsupported blocksize\n" ); goto failed_mount;"blocksize" contains the blocksize of the device here, and
sb->s_blocksizedoes contain the filesystem block size as saved in the super block.
You mean blocksize is the size of the ext2 block, and s_blocksize is the default size of the block device.
Quoted 6 lines
Xip does only work, if both do match
PAGE_SIZEbecause it does'nt support multiple calls todirect_accessin theget_xip_pageaddress space operation. Thus we check both here, actually this was changed from how it looks after your patch as a bugfix where our tester tried a 4k filesystem on a 2k blockdev.
Did I miss something?
However, the bdev block size may be changed with sb_set_blocksize. It doesn't actually have to match the hardware sector size -- if this does matter for XIP, then I think you need some other check here.
Re: [patch] ext2: xip check fix
Quoted 6 lines · Nick Piggin
Quoted 2 lines
Xip does only work, if both do match
PAGE_SIZEbecause it does'nt support multiple calls todirect_accessin theget_xip_pageaddress space operation. Thus we check both here, actually this was changed from how it looks after your patch as a bugfix where our tester tried a 4k filesystem on a 2k blockdev. Did I miss something?However, the bdev block size may be changed with
sb_set_blocksize. It doesn't actually have to match the hardware sector size -- if this does matter for XIP, then I think you need some other check here.
Hmmmmhh. For a bdev with PAGE_SIZE hardsect size, there is no other valid value then PAGE_SIZE that one could set it to. Or can it indeed be changed to a value greater then PAGE_SIZE or smaller then hardsect size?
Re: [patch] ext2: xip check fix
Quoted 8 lines · Carsten Otte
Quoted 7 lines · Nick Piggin
Quoted 6 lines
Xip does only work, if both do match
PAGE_SIZEbecause it does'nt support multiple calls todirect_accessin theget_xip_pageaddress space operation. Thus we check both here, actually this was changed from how it looks after your patch as a bugfix where our tester tried a 4k filesystem on a 2k blockdev.
Did I miss something?
Quoted 7 lines
Quoted 3 lines
However, the bdev block size may be changed with
sb_set_blocksize. It doesn't actually have to match the hardware sector size -- if this does matter for XIP, then I think you need some other check here.Hmmmmhh. For a bdev with
PAGE_SIZEhardsect size, there is no other valid value thenPAGE_SIZEthat one could set it to. Or can it indeed be changed to a value greater thenPAGE_SIZEor smaller then hardsect size?
It can't be made smaller (or larger, in current kernels). But you already get all that checking done for you -- both by checking that the filesystem blocksize == PAGE_SIZE, and by the error checking in sb_set_blocksize.
After my patch, we can do XIP in a hardsect size < PAGE_SIZE block device -- this seems to be a fine thing to do at least for the ramdisk code. Would this situation be problematic for existing drivers, and if so, in what way?
Thanks,
Nick
Carsten Otte
Acked-byRe: [patch] ext2: xip check fix
Quoted 4 lines · Nick Piggin
After my patch, we can do XIP in a hardsect size <
PAGE_SIZEblock device -- this seems to be a fine thing to do at least for the ramdisk code. Would this situation be problematic for existing drivers, and if so, in what way?
I have done some archeology, and our ancient CVS logs show this check was introduced in early 2005 into our 2.6.x. codebase. However, it existed way before, and was copied from our prehistorical ext2 split named xip2 back in the old days of 2.4.x where we did not really have a block device behind because that one was scamped into the file system in a very queer way. After all, I don't see any risk in removing the check. The only driver we have that does direct_access is drivers/s390/block/dcssblk.c, and that one only supports block_size == PAGE_SIZE. I think the patch should go into mainline.
Re: [patch] ext2: xip check fix
Quoted 11 lines · Carsten Otte
Quoted 4 lines · Nick Piggin
After my patch, we can do XIP in a hardsect size <
PAGE_SIZEblock device -- this seems to be a fine thing to do at least for the ramdisk code. Would this situation be problematic for existing drivers, and if so, in what way?I have done some archeology, and our ancient CVS logs show this check was introduced in early 2005 into our 2.6.x. codebase. However, it existed way before, and was copied from our prehistorical ext2 split named xip2 back in the old days of 2.4.x where we did not really have a block device behind because that one was scamped into the file system in a very queer way.
OK, thanks for taking a look at that. It will be helpful for testing XIP with my new ramdisk driver (did you see the patch?).
Quoted 4 lines
After all, I don't see any risk in removing the check. The only driver we have that does
direct_accessisdrivers/s390/block/dcssblk.c, and that one only supportsblock_size==PAGE_SIZE. I think the patch should go into mainline.
Actually another one's recently sprung up too (arch/powerpc/sysdev/axonram.c) but it looks like that one should be fine as it looks to be all simply memory mapped.
Quoted 1 line
Acked-by Carsten Otte <[email protected]>
Thanks! Andrew, would you queue this up for 2.6.25 please?
Re: [patch] ext2: xip check fix
Quoted 2 lines · Nick Piggin
OK, thanks for taking a look at that. It will be helpful for testing XIP with my new ramdisk driver (did you see the patch?).
I have'nt looked at it yet. I do appreciate it, I think it might broaden the user-base of this feature which is up to now s390 only due to the fact that the flash memory extensions have not been implemented (yet?). And it enables testing xip on other platforms. The patch is on my must-read list.
Quoted 3 lines
Actually another one's recently sprung up too (
arch/powerpc/sysdev/axonram.c) but it looks like that one should be fine as it looks to be all simply memory mapped.
Oh I know, the author's office is aproximately 500 meters away from mine, next building. I have'nt heared of anyone actually using that one lately.
cheers,
Carsten
Re: [patch] ext2: xip check fix
Quoted 9 lines · On Thursday 06 December 2007 04:24:20 Carsten Otte wrote:
Quoted 2 lines · Nick Piggin
OK, thanks for taking a look at that. It will be helpful for testing XIP with my new ramdisk driver (did you see the patch?).
I have'nt looked at it yet. I do appreciate it, I think it might broaden the user-base of this feature which is up to now s390 only due to the fact that the flash memory extensions have not been implemented (yet?). And it enables testing xip on other platforms. The patch is on my must-read list.
query: which feature is currently s390 only? (Execute In Place?)
Rob
Signature · 2 lines
"One of my most productive days was throwing away 1000 lines of code." - Ken Thompson.
Re: [patch] ext2: xip check fix
Quoted 7 lines
Quoted 5 lines
I have'nt looked at it yet. I do appreciate it, I think it might broaden the user-base of this feature which is up to now s390 only due to the fact that the flash memory extensions have not been implemented (yet?). And it enables testing xip on other platforms. The patch is on my must-read list.
query: which feature is currently s390 only? (Execute In Place?)
I think so. The filemap_xip.c functionality doesn't work for Flash memory yet. Flash memory doesn't have struct pages to back it up with which this stuff depends on.
Re: [patch] ext2: xip check fix
Quoted 11 lines · On Thursday 06 December 2007 21:22:25 Jared Hulbert wrote:
Quoted 7 lines
Quoted 5 lines
I have'nt looked at it yet. I do appreciate it, I think it might broaden the user-base of this feature which is up to now s390 only due to the fact that the flash memory extensions have not been implemented (yet?). And it enables testing xip on other platforms. The patch is on my must-read list.
query: which feature is currently s390 only? (Execute In Place?)
I think so. The
filemap_xip.cfunctionality doesn't work for Flash memory yet. Flash memory doesn't have struct pages to back it up with which this stuff depends on.
Um, trying to clarify: S390. Also known as zSeries, big iron machine, uses its own weird processor design rather than x86, x86-64, arm, or mips processors.
How does "struct page" enter into this?
What I want to know is, are you saying execute in place doesn't work on things like arm and mips? (I so, I was unaware of this. I heard about somebody getting it to work on a Nintendo DS:
http://forums.maxconsole.net/showthread.php?t=18668 )
Rob
Signature · 2 lines
"One of my most productive days was throwing away 1000 lines of code." - Ken Thompson.
Re: [patch] ext2: xip check fix
Quoted 17 lines · Rob Landley
Quoted 11 lines · On Thursday 06 December 2007 21:22:25 Jared Hulbert wrote:
Quoted 7 lines
Quoted 5 lines
I have'nt looked at it yet. I do appreciate it, I think it might broaden the user-base of this feature which is up to now s390 only due to the fact that the flash memory extensions have not been implemented (yet?). And it enables testing xip on other platforms. The patch is on my must-read list.
query: which feature is currently s390 only? (Execute In Place?)
I think so. The
filemap_xip.cfunctionality doesn't work for Flash memory yet. Flash memory doesn't have struct pages to back it up with which this stuff depends on.Um, trying to clarify: S390. Also known as zSeries, big iron machine, uses its own weird processor design rather than x86, x86-64, arm, or mips processors.
Quoted 6 lines
How does "struct page" enter into this?
What I want to know is, are you saying execute in place doesn't work on things like arm and mips? (I so, I was unaware of this. I heard about somebody getting it to work on a Nintendo DS:
http://forums.maxconsole.net/showthread.php?t=18668 )
It's just that the only device driver with XIP support for the moment is s390 (and an obscure powerpc one).
Now with my ramdisk patch, anybody can use it. That's just using regular RAM, but there is nothing preventing XIP backed by more exotic storage, provided the CPU can natively address and execute from it.
Re: [patch] ext2: xip check fix
In reply to Rob LandleyQuoted 3 lines
Um, trying to clarify: S390. Also known as zSeries, big iron machine, uses its own weird processor design rather than x86, x86-64, arm, or mips processors.
Right. filemap_xip.c allows for an XIP filesystem. The only filesystem that is supported is ext2. Even that requires a block device driver thingy, which I don't understand, that's specific to the s390.
Quoted 1 line
How does "struct page" enter into this?
Don't sweat it, it has to do with the way filemap_xip.c works.
Quoted 4 lines
What I want to know is, are you saying execute in place doesn't work on things like arm and mips? (I so, I was unaware of this. I heard about somebody getting it to work on a Nintendo DS:
http://forums.maxconsole.net/showthread.php?t=18668 )
XIP works fine on things like arm and mips. However there is mixed support in the mainline kernel for it. For example, you can build an XiP kernel image for arm since like 2.6.10 or 12. Also MTD has an XiP aware mode that protects XiP objects in flash from get screwed up during programs and erases. But there is no mainlined solution for XiP of applications from the filesystem. However there have been patches for cramfs to do this for years. They are kind of messy and keep getting rejected. I do have a solution in the works for this part of it - http://axfs.sf.net.
Re: [patch] ext2: xip check fix
In reply to Jared HulbertQuoted 3 lines · Jared Hulbert
I think so. The
filemap_xip.cfunctionality doesn't work for Flash memory yet. Flash memory doesn't have struct pages to back it up with which this stuff depends on.
Struct page is not the major issue. The primary problem is writing to the media (and I am not a flash expert at all, just relaying here): For some period of time, the flash memory is not usable and thus we need to make sure we can nuke the page table entries that we have in userland page tables. For that, we need a callback from the device so that it can ask to get its references back. Oh, and a put_xip_page counterpart to get_xip_page, so that the driver knows when it's safe to erase.
cheers,
Carsten
Re: [patch] ext2: xip check fix
Quoted 4 lines
Quoted 3 lines
I think so. The
filemap_xip.cfunctionality doesn't work for Flash memory yet. Flash memory doesn't have struct pages to back it up with which this stuff depends on.
Quoted 8 lines
Struct page is not the major issue. The primary problem is writing to the media (and I am not a flash expert at all, just relaying here):
For some period of time, the flash memory is not usable and thus we need to make sure we can nuke the page table entries that we have in userland page tables. For that, we need a callback from the device so that it can ask to get its references back. Oh, and aput_xip_pagecounterpart toget_xip_page, so that the driver knows when it's safe to erase.
Well... That's the biggest/hardest problem, yes. But not the first. First we got to tackle the easy read only case, which doesn't require any of that unpleasantness, yet which is used in a bunch of out of tree hacks.
Andrew Morton
maintainerRe: [patch] rd: support XIP
In reply to Nick Piggindrivers/block/brd.c static struct page *brd_insert_page(stru · line 110
Quoted 10 lines · On Tue, 4 Dec 2007 12:21:00 +0100 Nick Piggin <[email protected]> wrote:
drivers/block/brd.c +9
-0+* +* Cannot support XIP and highmem, because our ->direct_access +* routine for XIP must return memory that is always addressable. +* If XIP was reworked to use pfns and kmap throughout, this +* restriction might be able to be lifted. */ + gfp_flags =GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endif
A dubious tradeoff?
Re: [patch] rd: support XIP
Quoted 14 lines · Andrew Morton
drivers/block/brd.c static struct page *brd_insert_page(stru · line 110
Quoted 10 lines · On Tue, 4 Dec 2007 12:21:00 +0100 Nick Piggin <[email protected]> wrote:
drivers/block/brd.c +9
-0+* +* Cannot support XIP and highmem, because our ->direct_access +* routine for XIP must return memory that is always addressable. +* If XIP was reworked to use pfns and kmap throughout, this +* restriction might be able to be lifted. */ + gfp_flags =GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endif A dubious tradeoff?
On big highmem machines certainly. It may be somewhat useful on small memory systems... but having the config option there is nice for a VM developer without an s390 easily available ;)
But don't apply these XIP patches yet -- after a bit more testing I'm seeing some data corruption, so I'll have to work out what's going wrong with that first.
PATCH 1/1 mm: fix XIP file writes
Quoted 16 lines · Nick Piggin
Quoted 14 lines · Andrew Morton
Review
Quoted 10 lines · On Tue, 4 Dec 2007 12:21:00 +0100 Nick Piggin <[email protected]> wrote:
(quoted diff) +9
-0+ * + * Cannot support XIP and highmem, because our ->direct_access + * routine for XIP must return memory that is always addressable. + * If XIP was reworked to use pfns and kmap throughout, this + * restriction might be able to be lifted. */ + gfp_flags = GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endifA dubious tradeoff?
Quoted 7 lines
On big highmem machines certainly. It may be somewhat useful on small memory systems... but having the config option there is nice for a VM developer without an s390 easily available ;)
But don't apply these XIP patches yet -- after a bit more testing I'm seeing some data corruption, so I'll have to work out what's going wrong with that first.
Here we go. See, brd already found a bug ;)
Can you apply the ext2 XIP patch too? And I'll resend the brd XIP patch.
---
Writing to XIP files at a non-page-aligned offset results in data corruption because the writes were always sent to the start of the page.
---
mm/filemap_xip.c +1 -1
@@ -314,7 +314,7 @@ __xip_file_write(struct file *filp, consfault_in_pages_readable (buf, bytes); kaddr =kmap_atomic (page,KM_USER0 ); copied = bytes - -__copy_from_user_inatomic_nocache (kaddr, buf, bytes); +__copy_from_user_inatomic_nocache (kaddr + offset, buf, bytes);kunmap_atomic (kaddr,KM_USER0 );flush_dcache_page (page);
Re: [patch] mm: fix XIP file writes
Hi Nick,
Quoted 2 lines
Here we go. See, brd already found a bug ;)
Can you apply the ext2 XIP patch too? And I'll resend the brd XIP patch.
[...]
Quoted 2 lines
Writing to XIP files at a non-page-aligned offset results in data corruption because the writes were always sent to the start of the page.
[...]
mm/filemap_xip.c __xip_file_write(struct file *filp, cons · line 319
Quoted 8 lines
mm/filemap_xip.c +1
-1@@ -314,7 +314,7 @@ __xip_file_write(struct file *filp, consfault_in_pages_readable (buf, bytes); kaddr =kmap_atomic (page,KM_USER0 ); copied = bytes - -__copy_from_user_inatomic_nocache (kaddr, buf, bytes); +__copy_from_user_inatomic_nocache (kaddr + offset, buf, bytes);kunmap_atomic (kaddr,KM_USER0 );flush_dcache_page (page);
I asked myself why this problem never happened before. So I asked our testers to reproduce this problem on 2.6.23 and service levels. As the testcase did not trigger, I looked into the 2.6.23 code. This problem was introduced by commit 4a9e5ef1f4f15205e477817a5cefc34bd3f65f55 (mm: write iovec cleanup from Nick Piggin) during 2.6.24-rc:
--------snip-------
- copied = filemap_copy_from_user(page, offset, buf, bytes);
[...]
+ copied = bytes -
+ __copy_from_user_inatomic_nocache(kaddr, buf, bytes);
-------------------
So yes, its good to have xip on brd. It even tests your changes ;-) Good news is, that we dont need anything for stable.
Christian
Re: [patch] mm: fix XIP file writes
Quoted 29 lines · Christian Borntraeger
Hi Nick,
Quoted 2 lines
Here we go. See, brd already found a bug ;)
Can you apply the ext2 XIP patch too? And I'll resend the brd XIP patch.[...]
Quoted 2 lines
Writing to XIP files at a non-page-aligned offset results in data corruption because the writes were always sent to the start of the page.
[...]
mm/filemap_xip.c __xip_file_write(struct file *filp, cons · line 319
Quoted 8 lines
mm/filemap_xip.c +1
-1@@ -314,7 +314,7 @@ __xip_file_write(struct file *filp, consfault_in_pages_readable (buf, bytes); kaddr =kmap_atomic (page,KM_USER0 ); copied = bytes - -__copy_from_user_inatomic_nocache (kaddr, buf, bytes); +__copy_from_user_inatomic_nocache (kaddr + offset, buf, bytes);kunmap_atomic (kaddr,KM_USER0 );flush_dcache_page (page);I asked myself why this problem never happened before. So I asked our testers to reproduce this problem on 2.6.23 and service levels. As the testcase did not trigger, I looked into the 2.6.23 code. This problem was introduced by commit
4a9e5ef1f4f15205e477817a5cefc34bd3f65f55(mm: write iovec cleanup from Nick Piggin) during 2.6.24-rc:
--------snip-------(quoted diff) +2
-2- copied = filemap_copy_from_user(page, offset, buf, bytes); + copied = bytes - + __copy_from_user_inatomic_nocache(kaddr, buf, bytes); -------------------
Quoted 2 lines
So yes, its good to have xip on brd. It even tests your changes ;-) Good news is, that we dont need anything for stable.
Heh ;) that explains a lot. Thanks!
Re: [patch] rd: support XIP
In reply to Nick Piggindrivers/block/brd.c static struct page *brd_insert_page(stru · line 111
Quoted 5 lines · Nick Piggin
drivers/block/brd.c +4
-0+ gfp_flags =GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endif page =alloc_page (GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);
I think that should be alloc_page(gfp_flags), no?
Cheers,
Duane.
Signature · 1 line
"I never could learn to drink that blood and call it wine" - Bob Dylan
PATCH 1/1 rd: support XIP (updated)
Quoted 8 lines · Duane Griffin
Review
Quoted 5 lines · Nick Piggin
(quoted diff) +4
-0+ gfp_flags = GFP_NOIO | __GFP_ZERO; +#ifndef CONFIG_BLK_DEV_XIP + gfp_flags |= __GFP_HIGHMEM; +#endif page = alloc_page(GFP_NOIO | __GFP_HIGHMEM | __GFP_ZERO);I think that should be
alloc_page(gfp_flags), no?
Yes. Here is a resend. Andrew, please apply this one (has passed some testing with ext2 XIP).
--
Support direct_access XIP method with brd.
---
drivers/block/Kconfig +10 -0
@@ -346,6 +346,16 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. +config BLK_DEV_XIP + bool "Support XIP filesystems on RAM block device" + depends on BLK_DEV_RAM + default n + help + Support XIP filesystems (such as ext2 with XIP support on) on + top of block ram device. This will slightly enlarge the kernel, and + will prevent RAM block device backing store memory from being + allocated from highmem (only a problem for highmem systems). + config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media" depends on !UML
drivers/block/brd.c +38 -3
66 lines not on this page · raw message
Quoted 5 lines · Nick Piggin
drivers/block/brd.c new file +5
-0+static void copy_to_brd (struct brd_device *brd,const void *src, +sector_t sector,size_t n) +{ +struct page *page; +void *dst;
drivers/block/brd.c line 190
Quoted 10 lines
drivers/block/brd.c new file +10
-0+unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset); + page =brd_lookup_page (brd, sector); +BUG_ON (!page); + + dst =kmap_atomic (page,KM_USER1 ); +memcpy (dst + offset, src, copy); +kunmap_atomic (dst,KM_USER1 );
You're using kmap_atomic, but I see no reason you can't be preempted. Don't you need to at least disable preemption while you have stuff atomically kmapped?
Signature · 4 lines
Intel are signing my paycheques ... these opinions are still mine "Bill, look, we understand that you're interested in selling us this operating system, but compare it to ours. We can't possibly take such a retrograde step."
Quoted 12 lines · Matthew Wilcox
Quoted 11 lines · Nick Piggin
drivers/block/brd.c new file +11
-0+static void copy_to_brd (struct brd_device *brd,const void *src, +sector_t sector,size_t n) +{ +struct page *page; +void *dst; +unsigned int offset = (sector & (PAGE_SECTORS -1 )) <<SECTOR_SHIFT ; +size_t copy; + + copy =min ((unsigned long )n,PAGE_SIZE - offset); + page =brd_lookup_page (brd, sector); +BUG_ON (!page);
Quoted 8 lines
drivers/block/brd.c line 190
Quoted 4 lines
drivers/block/brd.c new file +4
-0+ + dst =kmap_atomic (page,KM_USER1 ); +memcpy (dst + offset, src, copy); +kunmap_atomic (dst,KM_USER1 );You're using
kmap_atomic, but I see no reason you can't be preempted. Don't you need to at least disable preemption while you have stuff atomically kmapped?
kmap_atomic() disables preemption through pagefault_disable().
Signature · 1 line
Jens Axboe