Improving mmap() support for !MMU further
Subsystems (12)
Hi Linus,
I'd like to improve mmap() support on !MMU still further by overloading struct backing_dev_info::memory_backed to hold flags describing what the backing device is capable of with respect to direct memory access.
(*) If bdi->memory_backed is 0, then the backing device is not accessible as
memory.
(*) If the bdi->memory_backed is non-zero, then it's a bit mask of the
following: #define BDI_NO_WRITEBACK 0x00000001 #define BDI_MEMORY_BACKED 0x00000001 #define BDI_CAN_MAP_DIRECT 0x00000002 #define BDI_CAN_MAP_COPY 0x00000004 #define BDI_CAN_READ_MAP VM_MAYREAD #define BDI_CAN_WRITE_MAP VM_MAYWRITE #define BDI_CAN_EXEC_MAP VM_MAYEXEC
(*) BDI_NO_WRITEBACK
Don't attempt to write back dirty pages.
(*) BDI_MEMORY_BACKED
The device is a memory or quasi-memory device.
(*) BDI_CAN_MAP_DIRECT
The device can be mapped directly. This means MAP_SHARED can be supported within the bounds of the specific mapping protection types.
(*) BDI_CAN_MAP_COPY
A copy into memory can be taken of all or part of the device using the file->f_op->read() method, and that can be passed to userspace as a MAP_PRIVATE mapping.
I/O devices, such as framebuffers, probably wouldn't set this as it doesn't make sense to take a copy. Storage devices (such as flash) and filesystems (such as RAMFS, CRAMFS or ROMFS) might, because it does make sense to take a copy for private writable mappings such as are made for executables.
(*) BDI_CAN_READ_MAP
(*) BDI_CAN_WRITE_MAP
(*) BDI_CAN_EXEC_MAP
Describe the capabilities of the backing device if mapped directly. RAMFS and RAMDISK could set all three, but CRAMFS, ROMFS and flash filesystems would probably not indicate writability and I/O devices such as frame buffers would not indicate executability.
By using such capability flags, no-MMU mmap() (and even MMU mmap()) could:
(1) Reject unsupportable shared mappings early on.
(2) Obviate the need for ROMFS hooks by determining what mappings can be mapped directly out of ROM and what must fall back to copies in RAM.
I've attached a sample mm/nommu.c mmap() implementation using this capability information for reference. Note the defaults for a chardev are direct mappings only and for a file are copied mappings only (copied mappings can be shared under some circumstances).
David
---
unsigned long do_mmap_pgoff(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long prot,
unsigned long flags,
unsigned long pgoff)
{
struct vm_list_struct *vml = NULL;
struct vm_area_struct *vma = NULL;
struct rb_node *rb;
unsigned long membacked, vm_flags;
void *result;
int ret;
/* do the simple checks first */
if (flags & MAP_FIXED || addr) {
printk(KERN_DEBUG "%d: Can't do fixed-address/overlay mmap of RAM\n",
current->pid);
return -EINVAL;
}
if ((flags & MAP_TYPE) != MAP_PRIVATE &&
(flags & MAP_TYPE) != MAP_SHARED)
return -EINVAL;
if (PAGE_ALIGN(len) == 0)
return addr;
if (len > TASK_SIZE)
return -EINVAL;
/* offset overflow? */
if ((pgoff + (len >> PAGE_SHIFT)) < pgoff)
return -EINVAL;
if (file) {
/* validate file mapping requests */
struct address_space *mapping;
/* files must support mmap */
if (!file->f_op || !file->f_op->mmap)
return -ENODEV;
/* work out if what we've got could possibly be shared
* - we support chardevs that provide their own "memory"
* - we support files/blockdevs that are memory backed
*/
mapping = file->f_mapping;
if (!mapping)
mapping = file->f_dentry->d_inode->i_mapping;
membacked = 0;
if (mapping && mapping->backing_dev_info)
membacked = mapping->backing_dev_info->memory_backed;
else if (S_ISCHR(file->f_dentry->d_inode->i_mode))
membacked = BDI_MEMORY_BACKED | BDI_CAN_MAP_DIRECT;
if (!membacked) {
if (S_ISREG(file->f_dentry->d_inode->i_mode) ||
S_ISBLK(file->f_dentry->d_inode->i_mode))
membacked = BDI_CAN_MAP_COPY;
}
/* can't map directly unless the driver or filesystem is
* willing to tell us where and can't copy and map if we can't
* read it
*/
if (!file->f_op->get_unmapped_area)
membacked &= ~BDI_CAN_MAP_DIRECT;
if (!file->f_op->read)
membacked &= ~BDI_CAN_MAP_COPY;
if (flags & MAP_SHARED) {
/* do checks for writing, appending and locking */
if ((prot & PROT_WRITE) &&
!(file->f_mode & FMODE_WRITE))
return -EACCES;
if (IS_APPEND(file->f_dentry->d_inode) &&
(file->f_mode & FMODE_WRITE))
return -EACCES;
if (locks_verify_locked(file->f_dentry->d_inode))
return -EAGAIN;
if (!(membacked & BDI_CAN_MAP_DIRECT))
return -ENODEV;
if (((prot & PROT_READ) && !(membacked & BDI_CAN_READ_MAP)) ||
((prot & PROT_WRITE) && !(membacked & BDI_CAN_WRITE_MAP)) ||
((prot & PROT_EXEC) && !(membacked & BDI_CAN_EXEC_MAP))
) {
printk("MAP_SHARED not completely supported on !MMU\n");
return -EINVAL;
}
/* we mustn't privatise shared mappings */
membacked &= ~BDI_CAN_MAP_COPY;
}
else {
/* we're going to read the file into private memory we
* allocate */
if (!(membacked & BDI_CAN_MAP_COPY))
return -ENODEV;
/* we don't permit a private writable mapping to be
* shared */
if (prot & PROT_WRITE)
membacked &= ~BDI_CAN_MAP_DIRECT;
}
/* handle executable mappings and implied executable
* mappings */
if (file->f_vfsmnt->mnt_flags & MNT_NOEXEC) {
if (prot & PROT_EXEC)
return -EPERM;
}
else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) {
/* handle implication of PROT_EXEC by PROT_READ */
if (current->personality & READ_IMPLIES_EXEC) {
if (membacked & BDI_CAN_EXEC_MAP)
prot |= PROT_EXEC;
}
}
else if ((prot & PROT_READ) &&
(prot & PROT_EXEC) &&
!(membacked & BDI_CAN_EXEC_MAP)
) {
/* backing file is not executable, try to copy */
membacked &= ~BDI_CAN_MAP_DIRECT;
}
}
else {
/* anonymous mappings are always memory backed and can be
* privately mapped
*/
membacked = BDI_MEMORY_BACKED | BDI_CAN_MAP_COPY;
/* handle PROT_EXEC implication by PROT_READ */
if ((prot & PROT_READ) &&
(current->personality & READ_IMPLIES_EXEC))
prot |= PROT_EXEC;
}
/* translate what we now know into VMA flags */
vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags);
vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
/* vm_flags |= mm->def_flags; */
if (!(membacked & BDI_CAN_MAP_DIRECT)) {
/* attempt to share any file segment that's mapped read-only */
if (file && !(prot & PROT_WRITE))
vm_flags |= VM_MAYSHARE;
}
else {
/* overlay a shareable mapping on the backing device or inode
* if possible - used for chardevs and ramfs/tmpfs/shmfs */
if (flags & MAP_SHARED)
vm_flags |= VM_MAYSHARE | VM_SHARED;
else if ((((vm_flags & membacked) ^ vm_flags) & BDI_MEMBACK_VMFLAGS) == 0)
vm_flags |= VM_MAYSHARE;
}
/* refuse to let anyone share private mappings with this process if
* it's being traced - otherwise breakpoints set in it may interfere
* with another untraced process
*/
if ((flags & MAP_PRIVATE) && (current->ptrace & PT_PTRACED))
vm_flags &= ~VM_MAYSHARE;
/* allow the security API to have its say */
ret = security_file_mmap(file, prot, flags);
if (ret)
return ret;
/* we're going to need to record the mapping if it works */
vml = kmalloc(sizeof(struct vm_list_struct), GFP_KERNEL);
if (!vml)
goto error_getting_vml;
memset(vml, 0, sizeof(*vml));
down_write(&nommu_vma_sem);
/* if we want to share, we need to check for VMAs created by other
* mmap() calls that overlap with our proposed mapping
* - we can only share with an exact match on most regular files
* - shared mappings on character devices and memory backed files are
* permitted to overlap inexactly as far as we are concerned for in
* these cases, sharing is handled in the driver or filesystem rather
* than here
*/
if (vm_flags & VM_MAYSHARE) {
unsigned long pglen = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
unsigned long vmpglen;
for (rb = rb_first(&nommu_vma_tree); rb; rb = rb_next(rb)) {
vma = rb_entry(rb, struct vm_area_struct, vm_rb);
if (!(vma->vm_flags & VM_MAYSHARE))
continue;
/* search for overlapping mappings on the same file */
if (vma->vm_file->f_dentry->d_inode != file->f_dentry->d_inode)
continue;
if (vma->vm_pgoff >= pgoff + pglen)
continue;
vmpglen = (vma->vm_end - vma->vm_start + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (pgoff >= vma->vm_pgoff + vmpglen)
continue;
/* handle inexact matches between mappings */
if (vmpglen != pglen || vma->vm_pgoff != pgoff) {
if (!membacked)
goto sharing_violation;
continue;
}
/* we've found a VMA we can share */
atomic_inc(&vma->vm_usage);
vml->vma = vma;
result = (void *) vma->vm_start;
goto shared;
}
}
vma = NULL;
/* obtain the address to map to - we verify (or select) it and ensure
* that it represents a valid section of the address space
* - this is the hook for quasi-memory character devices to tell us
* the location of a shared mapping
*/
if (file && file->f_op->get_unmapped_area) {
addr = file->f_op->get_unmapped_area(file, addr, len, pgoff,
flags);
if (IS_ERR((void *) addr)) {
ret = addr;
if (ret == (unsigned long) -ENOSYS)
ret = (unsigned long) -ENODEV;
goto error;
}
}
/* we're going to need a VMA struct as well */
vma = kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
if (!vma)
goto error_getting_vma;
memset(vma, 0, sizeof(*vma));
INIT_LIST_HEAD(&vma->anon_vma_node);
atomic_set(&vma->vm_usage, 1);
if (file)
get_file(file);
vma->vm_file = file;
vma->vm_flags = vm_flags;
vma->vm_start = addr;
vma->vm_end = addr + len;
vma->vm_pgoff = pgoff;
vml->vma = vma;
/* invoke the mapping routine on a file */
if (file) {
ret = file->f_op->mmap(file, vma);#ifdef DEBUG
printk("f_op->mmap() returned %d (st=%lx)\n",
ret, vma->vm_start);
#endif
result = (void *) vma->vm_start;
if (!ret)
goto done;
else if (ret != -ENOSYS)
goto error;
/* getting an ENOSYS error indicates that direct mmap isn't
* possible (as opposed to tried but failed) so we'll fall
* through to making a private copy of the data and mapping
* that if we can */
ret = -ENODEV;
if (vma->vm_flags & VM_SHARED)
goto error;
/* quick check that we're allowed to read for a private
* mapping */
if (!(membacked & BDI_CAN_MAP_COPY))
goto error;
}
/* allocate some memory to hold the mapping
* - note that this may not return a page-aligned address if the object
* we're allocating is smaller than a page
*/
ret = -ENOMEM;
result = kmalloc(len, GFP_KERNEL);
if (!result) {
printk("Allocation of length %lu from process %d failed\n",
len, current->pid);
show_free_areas();
goto error;
}
vma->vm_start = (unsigned long) result;
vma->vm_end = vma->vm_start + len;#ifdef WARN_ON_SLACK
if (len + WARN_ON_SLACK <= kobjsize(result))
printk("Allocation of %lu bytes from process %d has %lu bytes of slack\n",
len, current->pid, kobjsize(result) - len);
#endif
if (file) {
mm_segment_t old_fs = get_fs();
loff_t fpos;
fpos = pgoff;
fpos <<= PAGE_SHIFT;
set_fs(KERNEL_DS);
ret = file->f_op->read(file, (char *) result, len, &fpos);
set_fs(old_fs);
if (ret < 0)
goto error2;
if (ret < len)
memset(result + ret, 0, len - ret);
} else {
memset(result, 0, len);
}done:
if (!(vma->vm_flags & VM_SHARED)) {
realalloc += kobjsize(result);
askedalloc += len;
}
realalloc += kobjsize(vma);
askedalloc += sizeof(*vma);
current->mm->total_vm += len >> PAGE_SHIFT;
add_nommu_vma(vma);
shared:
realalloc += kobjsize(vml);
askedalloc += sizeof(*vml);
vml->next = current->mm->context.vmlist;
current->mm->context.vmlist = vml;
up_write(&nommu_vma_sem);
if (prot & PROT_EXEC)
flush_icache_range((unsigned long) result,
(unsigned long) result + len);#ifdef DEBUG
printk("do_mmap:\n");
show_process_blocks();#endif
return (unsigned long) result;
error2:
kfree(result);
error:
up_write(&nommu_vma_sem);
kfree(vml);
if (vma) {
fput(vma->vm_file);
kfree(vma);
}
return ret; sharing_violation:
up_write(&nommu_vma_sem);
printk("Attempt to share mismatched mappings\n");
kfree(vml);
return -EINVAL; error_getting_vma:
up_write(&nommu_vma_sem);
kfree(vml);
printk("Allocation of vml for %lu byte allocation from process %d failed\n",
len, current->pid);
show_free_areas();
return -ENOMEM; error_getting_vml:
printk("Allocation of vml for %lu byte allocation from process %d failed\n",
len, current->pid);
show_free_areas();
return -ENOMEM;
}Quoted 7 lines · David Howells
Hi Linus,
I'd like to improve
mmap()support on !MMU still further by overloading structbacking_dev_info::memory_backedto hold flags describing what the backing device is capable of with respect to direct memory access.
->memory_backed is already overloaded in various awkward ways. It would be good to fix that up first, then add enhancements in a later patch.
Furthermore I'd suggest that we remove ->memory_backed and simply add a bunch of new booleans to backing_dev_info rather than futzing with bitflags. Those booleans could be bitfields, of course.
And those fields should refer to device capabilities and not to device
characteristics: "what it can do" rather than "what it is". So "contributes to dirty memory" and "needs writeback" are good and "is a ram disk thingy" is bad.
So in 2.6.11 as-is, we have:
bdi->dirty_memory_acct Contributes to dirty memory accountingbdi->needs_writeback Needs writeback
Quoted 2 lines
(*) If
bdi->memory_backedis 0, then the backing device is not accessible as
memory.
Not sure what this means.
Quoted 4 lines
(*) If the
bdi->memory_backedis non-zero, then it's a bit mask of thefollowing: #define BDI_NO_WRITEBACK 0x00000001
Invert the sense of this, use ->needs_writeback
Quoted 1 line
#define BDI_MEMORY_BACKED 0x00000001
Lose this.
Quoted 1 line
#define BDI_CAN_MAP_DIRECT 0x00000002
bdi->can_map_direct
Quoted 1 line
#define BDI_CAN_MAP_COPY 0x00000004
bdi->can_map_copy
Quoted 3 lines
#define BDI_CAN_READ_MAP VM_MAYREAD #define BDI_CAN_WRITE_MAP VM_MAYWRITE #define BDI_CAN_EXEC_MAP VM_MAYEXEC
A separate field in backing_dev_info?
That `membacked' stuff in the !NOMMU implementation of do_mmap_pgoff() looks like it could benefit from some rework as a result of the above as well. It all looks rather inferential.
PATCH 1/2 BDI: Provide backing device capability information
The attached patch does two things:
(1) It gets rid of backing_dev_info::memory_backed and replaces it with a pair of boolean values:
(*) dirty_memory_acct
True if the pages associated with this backing device should be tracked by dirty page accounting.
(*) writeback_if_dirty
True if the pages associated with this backing device should have writepage() or writepages() invoked upon them to clean them.
(2) It adds a backing device capability mask that indicates what a backing device is capable of; currently only in regard to memory mapping facilities. These flags indicate whether a device can be mapped directly, whether it can be copied for a mapping, and whether direct mappings can be read, written and/or executed. This information is primarily aimed at improving no-MMU private mapping support.
---
warthog>diffstat -p1 memback-bdicap-2611rc4.diff
drivers/block/ll_rw_blk.c | 3 ++- drivers/block/rd.c | 14 ++++++++------ drivers/char/mem.c | 6 ++++++ fs/buffer.c | 2 +- fs/fs-writeback.c | 4 ++-- fs/hugetlbfs/inode.c | 5 +++-- fs/ramfs/inode.c | 7 +++++-- fs/sysfs/inode.c | 5 +++-- include/linux/backing-dev.h | 22 +++++++++++++++++++++- mm/filemap.c | 6 +++--- mm/page-writeback.c | 6 +++--- mm/readahead.c | 8 +++++--- mm/shmem.c | 7 ++++--- mm/swap_state.c | 5 +++-- 14 files changed, 69 insertions(+), 31 deletions(-)
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +21 -1
@@ -25,13 +25,33 @@ typedef int (congested_fn)(void *, int);struct backing_dev_info {unsigned long ra_pages;/* max readahead in PAGE_CACHE_SIZE units */ unsigned long state;/* Always use atomic bitops on this */ -int memory_backed;/* Cannot clean pages with writepage */ +unsigned short capabilities;/* Device capabilities */ +unsigned dirty_memory_acct :1 ;/* Contributes to dirty memory accounting */ +unsigned writeback_if_dirty :1 ;/* Dirty memory should be written back */ congested_fn *congested_fn;/* Function pointer if device is md/dm */ void *congested_data;/* Pointer to aux data for congested func */ void (*unplug_io_fn)(struct backing_dev_info *,struct page *);void *unplug_io_data; }; + +/* +* Flags in backing_dev_info::capability +* - These flags let !MMU mmap() govern direct device mapping vs immediate +* copying more easily for MAP_PRIVATE, especially for ROM filesystems +*/ +#define BDI_CAP_MAP_COPY 0x00000001/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000002/* Can be mapped directly (MAP_SHARED) */ +#define BDI_CAP_READ_MAP VM_MAYREAD/* Can be mapped for reading */ +#define BDI_CAP_WRITE_MAP VM_MAYWRITE/* Can be mapped for writing */ +#define BDI_CAP_EXEC_MAP VM_MAYEXEC/* Can be mapped for execution */ +#define BDI_CAP_VMFLAGS \ + (BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP) + +#if (BDI_CAP_MAP_DIRECT |BDI_CAP_COPY_FOR_MAP ) &BDI_CAP_VMFLAGS +#error please change backing_dev_info::capabilities flags +#endif +extern struct backing_dev_info default_backing_dev_info;void default_unplug_io_fn (struct backing_dev_info *bdi,struct page *page);
drivers/block/ll_rw_blk.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/ll_rw_blk.c +2 -1
@@ -238,7 +238,8 @@ void blk_queue_make_request(request_queu q->make_request_fn = mfn; q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE ; q->backing_dev_info.state =0 ; - q->backing_dev_info.memory_backed =0 ; + q->backing_dev_info.writeback_if_dirty =1 ; + q->backing_dev_info.dirty_memory_acct =1 ;blk_queue_max_sectors (q,MAX_SECTORS );blk_queue_hardsect_size (q,512 );blk_queue_dma_alignment (q,511 );
drivers/block/rd.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/rd.c +8 -6
@@ -324,9 +324,10 @@ static int rd_ioctl(struct inode *inode,* writeback and it does not contribute to dirty memory accounting. */ static struct backing_dev_info rd_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .ra_pages =0 ,/* No readahead */ + .dirty_memory_acct =0 ,/* Doesn't contribute to dirty memory */ + .writeback_if_dirty =0 , + .unplug_io_fn = default_unplug_io_fn, };/*
@@ -335,9 +336,10 @@ static struct backing_dev_info rd_backin* memory accounting. */ static struct backing_dev_info rd_file_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =0 ,/* Does contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .ra_pages =0 ,/* No readahead */ + .dirty_memory_acct =1 ,/* Does contribute to dirty memory */ + .writeback_if_dirty =1 , + .unplug_io_fn = default_unplug_io_fn, };static int rd_open (struct inode *inode,struct file *filp)
drivers/char/mem.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/char/mem.c +6 -0
@@ -23,6 +23,7 @@#include <linux/devfs_fs_kernel.h> #include <linux/ptrace.h> #include <linux/device.h> +#include <linux/backing-dev.h> #include <asm/uaccess.h> #include <asm/io.h>
@@ -613,6 +614,10 @@ static struct file_operations zero_fops .mmap = mmap_zero, }; +static struct backing_dev_info zero_bdi = { + .capabilities =BDI_CAP_MAP_COPY , +}; +static struct file_operations full_fops = { .llseek = full_lseek, .read = read_full,
@@ -659,6 +664,7 @@ static int memory_open(struct inode * inbreak ;#endif case 5 : + filp->f_mapping->backing_dev_info = &zero_bdi; filp->f_op = &zero_fops;break ;case 7 :
fs/buffer.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/buffer.c +1 -1
@@ -876,7 +876,7 @@ int __set_page_dirty_buffers(struct pageif (!TestSetPageDirty (page)) {spin_lock_irq (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ -if (!mapping->backing_dev_info->memory_backed) +if (mapping->backing_dev_info->dirty_memory_acct)inc_page_state (nr_dirty); radix_tree_tag_set(&mapping->page_tree, page_index(page),
fs/fs-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/fs-writeback.c +2 -2
@@ -315,7 +315,7 @@ sync_sb_inodes(struct super_block *sb, sstruct backing_dev_info *bdi = mapping->backing_dev_info;long pages_skipped; -if (bdi->memory_backed) { +if (!bdi->writeback_if_dirty) {list_move (&inode->i_list, &sb->s_dirty);if (sb == blockdev_superblock) {/*
@@ -566,7 +566,7 @@ int write_inode_now(struct inode *inode, .sync_mode = WB_SYNC_ALL, }; -if (inode->i_mapping->backing_dev_info->memory_backed) +if (!inode->i_mapping->backing_dev_info->writeback_if_dirty)return 0 ;might_sleep ();
fs/hugetlbfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/hugetlbfs/inode.c +3 -2
@@ -39,8 +39,9 @@ static struct inode_operations hugetlbfsstatic struct inode_operations hugetlbfs_inode_operations;static struct backing_dev_info hugetlbfs_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .ra_pages =0 ,/* No readahead */ + .dirty_memory_acct =0 ,/* Doesn't contribute to dirty memory */ + .writeback_if_dirty =0 , };int sysctl_hugetlb_shm_group;
fs/ramfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/ramfs/inode.c +5 -2
@@ -44,8 +44,11 @@ static struct inode_operations ramfs_filstatic struct inode_operations ramfs_dir_inode_operations;static struct backing_dev_info ramfs_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .ra_pages =0 ,/* No readahead */ + .dirty_memory_acct =0 ,/* Does not contribute to dirty memory */ + .writeback_if_dirty =0 , + .capabilities =BDI_CAP_MAP_DIRECT |BDI_CAP_MAP_COPY | +BDI_CAP_READ_MAP |BDI_CAP_WRITE_MAP |BDI_CAP_EXEC_MAP , };struct inode *ramfs_get_inode (struct super_block *sb,int mode,dev_t dev)
fs/sysfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/sysfs/inode.c +3 -2
@@ -22,8 +22,9 @@ static struct address_space_operations s };static struct backing_dev_info sysfs_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .ra_pages =0 ,/* No readahead */ + .writeback_if_dirty =0 , + .dirty_memory_acct =0 ,/* Doesn't contribute to dirty memory */ };struct inode *sysfs_new_inode (mode_t mode)
mm/filemap.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/filemap.c +3 -3
@@ -172,7 +172,7 @@ static int __filemap_fdatawrite_range(st .end = end, }; -if (mapping->backing_dev_info->memory_backed) +if (!mapping->backing_dev_info->writeback_if_dirty)return 0 ; ret =do_writepages (mapping, &wbc);
@@ -269,7 +269,7 @@ int sync_page_range(struct inode *inode,pgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping->backing_dev_info->writeback_if_dirty || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 ) {
@@ -295,7 +295,7 @@ int sync_page_range_nolock(struct inodepgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping->backing_dev_info->writeback_if_dirty || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 )
mm/page-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/page-writeback.c +3 -3
@@ -605,7 +605,7 @@ int __set_page_dirty_nobuffers(struct pa mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ BUG_ON (mapping2 != mapping); -if (!mapping->backing_dev_info->memory_backed) +if (mapping->backing_dev_info->dirty_memory_acct)inc_page_state (nr_dirty);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY );
@@ -691,7 +691,7 @@ int test_clear_page_dirty(struct page *ppage_index (page), PAGECACHE_TAG_DIRTY);spin_unlock_irqrestore (&mapping->tree_lock, flags); -if (!mapping->backing_dev_info->memory_backed) +if (mapping->backing_dev_info->dirty_memory_acct)dec_page_state (nr_dirty);return 1 ; }
@@ -722,7 +722,7 @@ int clear_page_dirty_for_io(struct pageif (mapping) {if (TestClearPageDirty (page)) { -if (!mapping->backing_dev_info->memory_backed) +if (mapping->backing_dev_info->dirty_memory_acct)dec_page_state (nr_dirty);return 1 ; }
mm/readahead.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/readahead.c +5 -3
@@ -21,9 +21,11 @@ void default_unplug_io_fn(struct backingEXPORT_SYMBOL (default_unplug_io_fn);struct backing_dev_info default_backing_dev_info = { - .ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE , - .state =0 , - .unplug_io_fn = default_unplug_io_fn, + .ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE , + .state =0 , + .dirty_memory_acct =1 , + .writeback_if_dirty =1 , + .unplug_io_fn = default_unplug_io_fn, };EXPORT_SYMBOL_GPL (default_backing_dev_info);
mm/shmem.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/shmem.c +4 -3
@@ -183,9 +183,10 @@ static struct inode_operations shmem_spestatic struct vm_operations_struct shmem_vm_ops;static struct backing_dev_info shmem_backing_dev_info = { - .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .ra_pages =0 ,/* No readahead */ + .dirty_memory_acct =0 ,/* Doesn't contribute to dirty memory */ + .writeback_if_dirty =0 , + .unplug_io_fn = default_unplug_io_fn, };static LIST_HEAD (shmem_swaplist);
mm/swap_state.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/swap_state.c +3 -2
@@ -29,8 +29,9 @@ static struct address_space_operations s };static struct backing_dev_info swap_backing_dev_info = { - .memory_backed =1 ,/* Does not contribute to dirty memory */ - .unplug_io_fn = swap_unplug_io_fn, + .dirty_memory_acct =0 ,/* Doesn't contribute to dirty memory */ + .writeback_if_dirty =0 , + .unplug_io_fn = swap_unplug_io_fn, };struct address_space swapper_space = {
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · David Howells
The attached patch does two things:
(1) It gets rid of
backing_dev_info::memory_backedand replaces it with a pair of boolean values:(*)
dirty_memory_acct
Quoted 7 lines
True if the pages associated with this backing device should be tracked by dirty page accounting.
(*)
writeback_if_dirtyTrue if the pages associated with this backing device should have
writepage()orwritepages()invoked upon them to clean them.
Cool, thanks.
include/linux/backing-dev.h typedef int (congested_fn)(void *, int); · line 44
Quoted 11 lines
(2) It adds a backing device capability mask that indicates what a backing device is capable of; currently only in regard to memory mapping facilities. These flags indicate whether a device can be mapped directly, whether it can be copied for a mapping, and whether direct mappings can be read, written and/or executed. This information is primarily aimed at improving no-MMU private mapping support.
...
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +2
-0+#define BDI_CAP_MAP_COPY 0x00000001/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000002/* Can be mapped directly (MAP_SHARED) */
Why not make these bitfields as well?
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 2 lines · Andrew Morton
Why not make these bitfields as well?
Side note: bitfields aren't exactly wonderful. They tend to generate worse code, and they make it much harder to work with combinations of flags (both testing and initializing). They also have architecture-specific bit-order and packing issues, which means that when coupled with device drivers etc they can be a major pain in the derriere.
Even apart from those issues, they can't sanely be atomically accessed, so in many cases they just aren't a good thing.
In contrast, just using a flag word and explicit bitmask has none of those problems, and it's often easy to abstract out things with a macro and/or inline function.
So don't go for bitfields "just because". It's generally a good idea to _require_ that the code in question has none of the potential problem spots even in _theory_, and in addition also show that bitfields really make the code look nicer. The downsides really can be that nasty.
Linus
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 6 lines · Linus Torvalds
Quoted 2 lines · Andrew Morton
Why not make these bitfields as well?
Side note: bitfields aren't exactly wonderful.
Yup. In this application the fields are initialised once (usually at compile time) and are never modified. So the compiler should be able to generate the same code as it would with an open-coded bit test. Which is about the only situation where we should use bitfields, IMO.
That being said, there aren't many backing_dev_info's in a system, so we won't be saving much memory. Some architectures will presumably generate faster code with plain old integers. You'd only ever lose if the backing_dev_info happened to straddle a cacheline boundary. It's marginal.
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 2 lines · Andrew Morton
Yup. In this application the fields are initialised once (usually at compile time) and are never modified.
That's not exactly so. The block layer appears to modify them. See blk_queue_make_request() in ll_rw_blk.c.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · David Howells
Quoted 2 lines · Andrew Morton
Yup. In this application the fields are initialised once (usually at compile time) and are never modified.
That's not exactly so. The block layer appears to modify them. See
blk_queue_make_request()inll_rw_blk.c.
That's a (strangely named) once-off setup thing.
Re: [PATCH 1/2] BDI: Provide backing device capability information
In reply to Andrew MortonQuoted 5 lines · Andrew Morton
include/linux/backing-dev.h typedef int (congested_fn)(void *, int); · line 44
Quoted 3 lines
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +2
-0+#define BDI_CAP_MAP_COPY 0x00000001/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000002/* Can be mapped directly (MAP_SHARED) */ Why not make these bitfields as well?
Because I want to copy the capabilities mask (including these variables) into a variable in the nommu mmap implementation and eliminate various bits from that variable under certain conditions.
Making these into bitfields would result in having to use three variables instead of just the one.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · David Howells
Quoted 5 lines · Andrew Morton
include/linux/backing-dev.h typedef int (congested_fn)(void *, int); · line 44
Quoted 3 lines
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +2
-0+#define BDI_CAP_MAP_COPY 0x00000001/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000002/* Can be mapped directly (MAP_SHARED) */ Why not make these bitfields as well?
Quoted 6 lines
Because I want to copy the capabilities mask (including these variables) into a variable in the nommu mmap implementation and eliminate various bits from that variable under certain conditions.
Making these into bitfields would result in having to use three variables instead of just the one.
Well let's do one or the other, and not have it half-and-half, please.
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 4 lines · Andrew Morton
Quoted 2 lines
Making these into bitfields would result in having to use three variables instead of just the one.
Well let's do one or the other, and not have it half-and-half, please.
So I should fold the two other bitfields back into the capabilities mask and make it an unsigned long.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 10 lines · David Howells
Quoted 4 lines · Andrew Morton
Quoted 2 lines
Making these into bitfields would result in having to use three variables instead of just the one.
Well let's do one or the other, and not have it half-and-half, please.
So I should fold the two other bitfields back into the capabilities mask and make it an unsigned long.
I suppose so. Although unsigned int would be preferable.
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 4 lines · Andrew Morton
Quoted 2 lines
So I should fold the two other bitfields back into the capabilities mask and make it an unsigned long.
I suppose so. Although unsigned int would be preferable.
Any particular reason? It's mixed in with other unsigned longs and pointers after all...
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 10 lines · David Howells
Quoted 4 lines · Andrew Morton
Quoted 2 lines
So I should fold the two other bitfields back into the capabilities mask and make it an unsigned long.
I suppose so. Although unsigned int would be preferable.
Any particular reason? It's mixed in with other unsigned longs and pointers after all...
Just that it's the natural wordsize of the machine, and uses less storage.
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 4 lines · Andrew Morton
Quoted 2 lines
Any particular reason? It's mixed in with other unsigned longs and pointers after all...
Just that it's the natural wordsize of the machine, and uses less storage.
Making it unsigned long on a 32-bit machine will make no difference. Making it unsigned int on a 64-bit machine will waste four bytes.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 11 lines · David Howells
Quoted 4 lines · Andrew Morton
Quoted 2 lines
Any particular reason? It's mixed in with other unsigned longs and pointers after all...
Just that it's the natural wordsize of the machine, and uses less storage.
Making it unsigned long on a 32-bit machine will make no difference. Making it unsigned int on a 64-bit machine will waste four bytes.
No, it won't waste any bytes at all. It won't save any either.
But if someone comes along later and wants to add another int to that structure, they won't know that your field was needlessly made a long, and they'll then waste another eight bytes.
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · Andrew Morton
Quoted 3 lines
Making it unsigned long on a 32-bit machine will make no difference. Making it unsigned int on a 64-bit machine will waste four bytes.
No, it won't waste any bytes at all. It won't save any either.
But if someone comes along later and wants to add another int to that structure, they won't know that your field was needlessly made a long, and they'll then waste another eight bytes.
Okay... Fair enough.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
In reply to Andrew MortonThe attached patch replaces backing_dev_info::memory_backed with capabilitied bitmap. The capabilities available include:
(*) BDI_CAP_ACCOUNT_DIRTY
Set if the pages associated with this backing device should be tracked by dirty page accounting.
(*) BDI_CAP_WRITEBACK_DIRTY
Set if dirty pages associated with this backing device should have writepage() or writepages() invoked upon them to clean them.
(*) Capability markers that indicate what a backing device is capable of
with regard to memory mapping facilities. These flags indicate whether a device can be mapped directly, whether it can be copied for a mapping, and whether direct mappings can be read, written and/or executed. This information is primarily aimed at improving no-MMU private mapping support.
The patch alco provides convenience functions for determining the dirty-page capabilities available on backing devices directly or on the backing devices associated with a mapping. These are provided to keep line length down when checking for the capabilities.
---
warthog>diffstat -p1 memback-bdicap-2611rc4-2.diff
drivers/block/ll_rw_blk.c | 2 +- drivers/block/rd.c | 6 +++--- drivers/char/mem.c | 6 ++++++ fs/buffer.c | 2 +- fs/fs-writeback.c | 4 ++-- fs/hugetlbfs/inode.c | 2 +- fs/ramfs/inode.c | 3 ++- fs/sysfs/inode.c | 2 +- include/linux/backing-dev.h | 41 ++++++++++++++++++++++++++++++++++++++++- mm/filemap.c | 6 +++--- mm/page-writeback.c | 6 +++--- mm/readahead.c | 1 + mm/shmem.c | 4 ++-- mm/swap_state.c | 2 +- 14 files changed, 67 insertions(+), 20 deletions(-)
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +40 -1
@@ -25,13 +25,39 @@ typedef int (congested_fn)(void *, int);struct backing_dev_info {unsigned long ra_pages;/* max readahead in PAGE_CACHE_SIZE units */ unsigned long state;/* Always use atomic bitops on this */ -int memory_backed;/* Cannot clean pages with writepage */ +unsigned int capabilities;/* Device capabilities */ congested_fn *congested_fn;/* Function pointer if device is md/dm */ void *congested_data;/* Pointer to aux data for congested func */ void (*unplug_io_fn)(struct backing_dev_info *,struct page *);void *unplug_io_data; }; + +/* +* Flags in backing_dev_info::capability +* - The first two flags control whether dirty pages will contribute to the +* VM's accounting and whether writepages() should be called for dirty pages +* (something that would not, for example, be appropriate for ramfs) +* - These flags let !MMU mmap() govern direct device mapping vs immediate +* copying more easily for MAP_PRIVATE, especially for ROM filesystems +*/ +#define BDI_CAP_ACCOUNT_DIRTY 0x00000001/* Dirty pages should contribute to accounting */ +#define BDI_CAP_WRITEBACK_DIRTY 0x00000002/* Dirty pages should be written back */ +#define BDI_CAP_MAP_COPY 0x00000004/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000008/* Can be mapped directly (MAP_SHARED) */ +#define BDI_CAP_READ_MAP 0x00000010/* Can be mapped for reading */ +#define BDI_CAP_WRITE_MAP 0x00000020/* Can be mapped for writing */ +#define BDI_CAP_EXEC_MAP 0x00000040/* Can be mapped for execution */ +#define BDI_CAP_VMFLAGS \ + (BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP) + +#if defined(VM_MAYREAD ) && \ + (BDI_CAP_READ_MAP !=VM_MAYREAD || \ +BDI_CAP_WRITE_MAP !=VM_MAYWRITE || \ +BDI_CAP_EXEC_MAP !=VM_MAYEXEC ) +#error please change backing_dev_info::capabilities flags +#endif +extern struct backing_dev_info default_backing_dev_info;void default_unplug_io_fn (struct backing_dev_info *bdi,struct page *page);
@@ -62,4 +88,17 @@ static inline int bdi_rw_congested(struc (1 << BDI_write_congested)); } +#define bdi_cap_writeback_dirty (bdi) \ + ((bdi)->capabilities & BDI_CAP_WRITEBACK_DIRTY) + +#define bdi_cap_account_dirty (bdi) \ + ((bdi)->capabilities & BDI_CAP_ACCOUNT_DIRTY) + +#define mapping_cap_writeback_dirty (mapping) \ + bdi_cap_writeback_dirty((mapping)->backing_dev_info) + +#define mapping_cap_account_dirty (mapping) \ + bdi_cap_account_dirty((mapping)->backing_dev_info) + +#endif /* _LINUX_BACKING_DEV_H */
drivers/block/ll_rw_blk.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/ll_rw_blk.c +1 -1
@@ -238,7 +238,7 @@ void blk_queue_make_request(request_queu q->make_request_fn = mfn; q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE ; q->backing_dev_info.state =0 ; - q->backing_dev_info.memory_backed =0 ; + q->backing_dev_info.capabilities =BDI_CAP_ACCOUNT_DIRTY |BDI_CAP_WRITEBACK_DIRTY ;blk_queue_max_sectors (q,MAX_SECTORS );blk_queue_hardsect_size (q,512 );blk_queue_dma_alignment (q,511 );
drivers/block/rd.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/rd.c +3 -3
@@ -325,7 +325,7 @@ static int rd_ioctl(struct inode *inode,*/ static struct backing_dev_info rd_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =0 ,/* Doesn't contribute to dirty memory */ .unplug_io_fn = default_unplug_io_fn, };
@@ -336,8 +336,8 @@ static struct backing_dev_info rd_backin*/ static struct backing_dev_info rd_file_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =0 ,/* Does contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .capabilities =1 ,/* Does contribute to dirty memory */ + .unplug_io_fn = default_unplug_io_fn, };static int rd_open (struct inode *inode,struct file *filp)
drivers/char/mem.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/char/mem.c +6 -0
@@ -23,6 +23,7 @@#include <linux/devfs_fs_kernel.h> #include <linux/ptrace.h> #include <linux/device.h> +#include <linux/backing-dev.h> #include <asm/uaccess.h> #include <asm/io.h>
@@ -613,6 +614,10 @@ static struct file_operations zero_fops .mmap = mmap_zero, }; +static struct backing_dev_info zero_bdi = { + .capabilities =BDI_CAP_MAP_COPY , +}; +static struct file_operations full_fops = { .llseek = full_lseek, .read = read_full,
@@ -659,6 +664,7 @@ static int memory_open(struct inode * inbreak ;#endif case 5 : + filp->f_mapping->backing_dev_info = &zero_bdi; filp->f_op = &zero_fops;break ;case 7 :
fs/buffer.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/buffer.c +1 -1
@@ -876,7 +876,7 @@ int __set_page_dirty_buffers(struct pageif (!TestSetPageDirty (page)) {spin_lock_irq (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))inc_page_state (nr_dirty); radix_tree_tag_set(&mapping->page_tree, page_index(page),
fs/fs-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/fs-writeback.c +2 -2
@@ -315,7 +315,7 @@ sync_sb_inodes(struct super_block *sb, sstruct backing_dev_info *bdi = mapping->backing_dev_info;long pages_skipped; -if (bdi->memory_backed) { +if (!bdi_cap_writeback_dirty (bdi)) {list_move (&inode->i_list, &sb->s_dirty);if (sb == blockdev_superblock) {/*
@@ -566,7 +566,7 @@ int write_inode_now(struct inode *inode, .sync_mode = WB_SYNC_ALL, }; -if (inode->i_mapping->backing_dev_info->memory_backed) +if (!mapping_cap_writeback_dirty (inode->i_mapping))return 0 ;might_sleep ();
fs/hugetlbfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/hugetlbfs/inode.c +1 -1
@@ -40,7 +40,7 @@ static struct inode_operations hugetlbfsstatic struct backing_dev_info hugetlbfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =0 ,/* Doesn't contribute to dirty memory */ };int sysctl_hugetlb_shm_group;
fs/ramfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/ramfs/inode.c +2 -1
@@ -45,7 +45,8 @@ static struct inode_operations ramfs_dirstatic struct backing_dev_info ramfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_MAP_DIRECT |BDI_CAP_MAP_COPY | +BDI_CAP_READ_MAP |BDI_CAP_WRITE_MAP |BDI_CAP_EXEC_MAP , };struct inode *ramfs_get_inode (struct super_block *sb,int mode,dev_t dev)
fs/sysfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/sysfs/inode.c +1 -1
@@ -23,7 +23,7 @@ static struct address_space_operations sstatic struct backing_dev_info sysfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =0 ,/* Doesn't contribute to dirty memory */ };struct inode *sysfs_new_inode (mode_t mode)
mm/filemap.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/filemap.c +3 -3
@@ -172,7 +172,7 @@ static int __filemap_fdatawrite_range(st .end = end, }; -if (mapping->backing_dev_info->memory_backed) +if (!mapping_cap_writeback_dirty (mapping))return 0 ; ret =do_writepages (mapping, &wbc);
@@ -269,7 +269,7 @@ int sync_page_range(struct inode *inode,pgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping_cap_writeback_dirty (mapping) || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 ) {
@@ -295,7 +295,7 @@ int sync_page_range_nolock(struct inodepgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping_cap_writeback_dirty (mapping) || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 )
mm/page-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/page-writeback.c +3 -3
@@ -605,7 +605,7 @@ int __set_page_dirty_nobuffers(struct pa mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ BUG_ON (mapping2 != mapping); -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))inc_page_state (nr_dirty);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY );
@@ -691,7 +691,7 @@ int test_clear_page_dirty(struct page *ppage_index (page), PAGECACHE_TAG_DIRTY);spin_unlock_irqrestore (&mapping->tree_lock, flags); -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))dec_page_state (nr_dirty);return 1 ; }
@@ -722,7 +722,7 @@ int clear_page_dirty_for_io(struct pageif (mapping) {if (TestClearPageDirty (page)) { -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))dec_page_state (nr_dirty);return 1 ; }
mm/readahead.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/readahead.c +1 -0
@@ -23,6 +23,7 @@ EXPORT_SYMBOL(default_unplug_io_fn);struct backing_dev_info default_backing_dev_info = { .ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE , .state =0 , + .capabilities =BDI_CAP_ACCOUNT_DIRTY |BDI_CAP_WRITEBACK_DIRTY , .unplug_io_fn = default_unplug_io_fn, };EXPORT_SYMBOL_GPL (default_backing_dev_info);
mm/shmem.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/shmem.c +2 -2
@@ -184,8 +184,8 @@ static struct vm_operations_struct shmemstatic struct backing_dev_info shmem_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .capabilities =0 ,/* Doesn't contribute to dirty memory */ + .unplug_io_fn = default_unplug_io_fn, };static LIST_HEAD (shmem_swaplist);
mm/swap_state.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/swap_state.c +1 -1
@@ -29,7 +29,7 @@ static struct address_space_operations s };static struct backing_dev_info swap_backing_dev_info = { - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =0 ,/* Doesn't contribute to dirty memory */ .unplug_io_fn = swap_unplug_io_fn, };
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 2 lines
The attached patch replaces
backing_dev_info::memory_backedwith capabilitied bitmap. The capabilities available include:
Wouldn't it be better to reverse the meaning of BDI_CAP_ACCOUNT_DIRTY and BDI_CAP_WRITEBACK_DIRTY (BDI_CAP_NO_ACCOUNT_DIRTY...)? That way out of tree filesystems that implicitly zero bdi->memory_backed wouldn't _silently_ break. E.g. fuse does this, though it would not actually break since it doesn't dirty any pages currently. I have no idea whether there are other filesystems that are affected.
Miklos
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · Miklos Szeredi
Quoted 2 lines
The attached patch replaces
backing_dev_info::memory_backedwith capabilitied bitmap. The capabilities available include:Wouldn't it be better to reverse the meaning of
BDI_CAP_ACCOUNT_DIRTYandBDI_CAP_WRITEBACK_DIRTY(BDI_CAP_NO_ACCOUNT_DIRTY...)? That way out of tree filesystems that implicitly zerobdi->memory_backedwouldn't _silently_ break. E.g. fuse does this, though it would not actually break since it doesn't dirty any pages currently. I have no idea whether there are other filesystems that are affected.
It shouldn't silently break... It will refuse to compile. I renamed "memory_backed" to "capabilities".
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines
Quoted 6 lines
Wouldn't it be better to reverse the meaning of
BDI_CAP_ACCOUNT_DIRTYandBDI_CAP_WRITEBACK_DIRTY(BDI_CAP_NO_ACCOUNT_DIRTY...)? That way out of tree filesystems that implicitly zerobdi->memory_backedwouldn't _silently_ break. E.g. fuse does this, though it would not actually break since it doesn't dirty any pages currently. I have no idea whether there are other filesystems that are affected.It shouldn't silently break... It will refuse to compile. I renamed "
memory_backed" to "capabilities".
This will silently break:
static struct backing_dev_info my_bdi = {
.ra_pages = MY_RA,
.unplug_io_fn = default_unplug_io_fn,
};
Miklos
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 9 lines · Miklos Szeredi
Quoted 2 lines
It shouldn't silently break... It will refuse to compile. I renamed "
memory_backed" to "capabilities".This will silently break:
static struct
backing_dev_infomy_bdi= {
.ra_pages =MY_RA,
.unplug_io_fn =default_unplug_io_fn,
};
Sorry, yes. Obvious. Ugh. Andrew Morton suggested flipping the logic, and although it was in conjunction with turning the concepts into bitfields, it still stands here.
David
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 3 lines
Sorry, yes. Obvious. Ugh. Andrew Morton suggested flipping the logic, and although it was in conjunction with turning the concepts into bitfields, it still stands here.
OK, obviously Andrew has the final word in this. I just suggested that it might be safer to have the logic flipped back.
Miklos
Re: [PATCH 1/2] BDI: Provide backing device capability information
Quoted 8 lines · Miklos Szeredi
Quoted 4 lines
Sorry, yes. Obvious. Ugh. Andrew Morton suggested flipping the logic, and although it was in conjunction with turning the concepts into bitfields, it still stands here.
OK, obviously Andrew has the final word in this. I just suggested that it might be safer to have the logic flipped back.
Experience indicates that it's safer to ignore anything I say on this topic.
Yeah, it would be better to not flip the logic.
PATCH 1/2 BDI: Provide backing device capability information [try #3]
The attached patch replaces backing_dev_info::memory_backed with capabilitied bitmap. The capabilities available include:
(*) BDI_CAP_NO_ACCT_DIRTY
Set if the pages associated with this backing device should not be tracked by the dirty page accounting.
(*) BDI_CAP_NO_WRITEBACK
Set if dirty pages associated with this backing device should not have writepage() or writepages() invoked upon them to clean them.
(*) Capability markers that indicate what a backing device is capable of
with regard to memory mapping facilities. These flags indicate whether a device can be mapped directly, whether it can be copied for a mapping, and whether direct mappings can be read, written and/or executed. This information is primarily aimed at improving no-MMU private mapping support.
The patch also provides convenience functions for determining the dirty-page capabilities available on backing devices directly or on the backing devices associated with a mapping. These are provided to keep line length down when checking for the capabilities.
---
include/linux/backing-dev.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/backing-dev.h +40 -1
@@ -25,13 +25,39 @@ typedef int (congested_fn)(void *, int);struct backing_dev_info {unsigned long ra_pages;/* max readahead in PAGE_CACHE_SIZE units */ unsigned long state;/* Always use atomic bitops on this */ -int memory_backed;/* Cannot clean pages with writepage */ +unsigned int capabilities;/* Device capabilities */ congested_fn *congested_fn;/* Function pointer if device is md/dm */ void *congested_data;/* Pointer to aux data for congested func */ void (*unplug_io_fn)(struct backing_dev_info *,struct page *);void *unplug_io_data; }; + +/* +* Flags in backing_dev_info::capability +* - The first two flags control whether dirty pages will contribute to the +* VM's accounting and whether writepages() should be called for dirty pages +* (something that would not, for example, be appropriate for ramfs) +* - These flags let !MMU mmap() govern direct device mapping vs immediate +* copying more easily for MAP_PRIVATE, especially for ROM filesystems +*/ +#define BDI_CAP_NO_ACCT_DIRTY 0x00000001/* Dirty pages shouldn't contribute to accounting */ +#define BDI_CAP_NO_WRITEBACK 0x00000002/* Don't write pages back */ +#define BDI_CAP_MAP_COPY 0x00000004/* Copy can be mapped (MAP_PRIVATE) */ +#define BDI_CAP_MAP_DIRECT 0x00000008/* Can be mapped directly (MAP_SHARED) */ +#define BDI_CAP_READ_MAP 0x00000010/* Can be mapped for reading */ +#define BDI_CAP_WRITE_MAP 0x00000020/* Can be mapped for writing */ +#define BDI_CAP_EXEC_MAP 0x00000040/* Can be mapped for execution */ +#define BDI_CAP_VMFLAGS \ + (BDI_CAP_READ_MAP | BDI_CAP_WRITE_MAP | BDI_CAP_EXEC_MAP) + +#if defined(VM_MAYREAD ) && \ + (BDI_CAP_READ_MAP !=VM_MAYREAD || \ +BDI_CAP_WRITE_MAP !=VM_MAYWRITE || \ +BDI_CAP_EXEC_MAP !=VM_MAYEXEC ) +#error please change backing_dev_info::capabilities flags +#endif +extern struct backing_dev_info default_backing_dev_info;void default_unplug_io_fn (struct backing_dev_info *bdi,struct page *page);
@@ -62,4 +88,17 @@ static inline int bdi_rw_congested(struc (1 << BDI_write_congested)); } +#define bdi_cap_writeback_dirty (bdi) \ + (!((bdi)->capabilities & BDI_CAP_NO_WRITEBACK)) + +#define bdi_cap_account_dirty (bdi) \ + (!((bdi)->capabilities & BDI_CAP_NO_ACCT_DIRTY)) + +#define mapping_cap_writeback_dirty (mapping) \ + bdi_cap_writeback_dirty((mapping)->backing_dev_info) + +#define mapping_cap_account_dirty (mapping) \ + bdi_cap_account_dirty((mapping)->backing_dev_info) + +#endif /* _LINUX_BACKING_DEV_H */
drivers/block/ll_rw_blk.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/ll_rw_blk.c +1 -1
@@ -238,7 +238,7 @@ void blk_queue_make_request(request_queu q->make_request_fn = mfn; q->backing_dev_info.ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE ; q->backing_dev_info.state =0 ; - q->backing_dev_info.memory_backed =0 ; + q->backing_dev_info.capabilities =BDI_CAP_MAP_COPY ;blk_queue_max_sectors (q,MAX_SECTORS );blk_queue_hardsect_size (q,512 );blk_queue_dma_alignment (q,511 );
drivers/block/rd.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/block/rd.c +2 -2
@@ -325,7 +325,7 @@ static int rd_ioctl(struct inode *inode,*/ static struct backing_dev_info rd_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK |BDI_CAP_MAP_COPY , .unplug_io_fn = default_unplug_io_fn, };
@@ -336,7 +336,7 @@ static struct backing_dev_info rd_backin*/ static struct backing_dev_info rd_file_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =0 ,/* Does contribute to dirty memory */ + .capabilities =BDI_CAP_MAP_COPY ,/* Does contribute to dirty memory */ .unplug_io_fn = default_unplug_io_fn, };
drivers/char/mem.c renamed from warthog/kernels/linux-2.6.11-rc4/drivers/char/mem.c +6 -0
@@ -23,6 +23,7 @@#include <linux/devfs_fs_kernel.h> #include <linux/ptrace.h> #include <linux/device.h> +#include <linux/backing-dev.h> #include <asm/uaccess.h> #include <asm/io.h>
@@ -613,6 +614,10 @@ static struct file_operations zero_fops .mmap = mmap_zero, }; +static struct backing_dev_info zero_bdi = { + .capabilities =BDI_CAP_MAP_COPY , +}; +static struct file_operations full_fops = { .llseek = full_lseek, .read = read_full,
@@ -659,6 +664,7 @@ static int memory_open(struct inode * inbreak ;#endif case 5 : + filp->f_mapping->backing_dev_info = &zero_bdi; filp->f_op = &zero_fops;break ;case 7 :
fs/buffer.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/buffer.c +1 -1
@@ -876,7 +876,7 @@ int __set_page_dirty_buffers(struct pageif (!TestSetPageDirty (page)) {spin_lock_irq (&mapping->tree_lock);if (page->mapping) {/* Race with truncate? */ -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))inc_page_state (nr_dirty); radix_tree_tag_set(&mapping->page_tree, page_index(page),
fs/fs-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/fs-writeback.c +2 -2
@@ -315,7 +315,7 @@ sync_sb_inodes(struct super_block *sb, sstruct backing_dev_info *bdi = mapping->backing_dev_info;long pages_skipped; -if (bdi->memory_backed) { +if (!bdi_cap_writeback_dirty (bdi)) {list_move (&inode->i_list, &sb->s_dirty);if (sb == blockdev_superblock) {/*
@@ -566,7 +566,7 @@ int write_inode_now(struct inode *inode, .sync_mode = WB_SYNC_ALL, }; -if (inode->i_mapping->backing_dev_info->memory_backed) +if (!mapping_cap_writeback_dirty (inode->i_mapping))return 0 ;might_sleep ();
fs/hugetlbfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/hugetlbfs/inode.c +1 -1
@@ -40,7 +40,7 @@ static struct inode_operations hugetlbfsstatic struct backing_dev_info hugetlbfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK , };int sysctl_hugetlb_shm_group;
fs/ramfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/ramfs/inode.c +3 -1
@@ -45,7 +45,9 @@ static struct inode_operations ramfs_dirstatic struct backing_dev_info ramfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK | +BDI_CAP_MAP_DIRECT |BDI_CAP_MAP_COPY | +BDI_CAP_READ_MAP |BDI_CAP_WRITE_MAP |BDI_CAP_EXEC_MAP , };struct inode *ramfs_get_inode (struct super_block *sb,int mode,dev_t dev)
fs/sysfs/inode.c renamed from warthog/kernels/linux-2.6.11-rc4/fs/sysfs/inode.c +1 -1
@@ -23,7 +23,7 @@ static struct address_space_operations sstatic struct backing_dev_info sysfs_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK , };struct inode *sysfs_new_inode (mode_t mode)
mm/filemap.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/filemap.c +3 -3
@@ -172,7 +172,7 @@ static int __filemap_fdatawrite_range(st .end = end, }; -if (mapping->backing_dev_info->memory_backed) +if (!mapping_cap_writeback_dirty (mapping))return 0 ; ret =do_writepages (mapping, &wbc);
@@ -269,7 +269,7 @@ int sync_page_range(struct inode *inode,pgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping_cap_writeback_dirty (mapping) || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 ) {
@@ -295,7 +295,7 @@ int sync_page_range_nolock(struct inodepgoff_t end = (pos + count -1 ) >>PAGE_CACHE_SHIFT ;int ret; -if (mapping->backing_dev_info->memory_backed || !count) +if (!mapping_cap_writeback_dirty (mapping) || !count)return 0 ; ret =filemap_fdatawrite_range (mapping, pos, pos + count -1 );if (ret ==0 )
mm/page-writeback.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/page-writeback.c +3 -3
@@ -605,7 +605,7 @@ int __set_page_dirty_nobuffers(struct pa mapping2 =page_mapping (page);if (mapping2) {/* Race with truncate? */ BUG_ON (mapping2 != mapping); -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))inc_page_state (nr_dirty);radix_tree_tag_set (&mapping->page_tree,page_index (page),PAGECACHE_TAG_DIRTY );
@@ -691,7 +691,7 @@ int test_clear_page_dirty(struct page *ppage_index (page), PAGECACHE_TAG_DIRTY);spin_unlock_irqrestore (&mapping->tree_lock, flags); -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))dec_page_state (nr_dirty);return 1 ; }
@@ -722,7 +722,7 @@ int clear_page_dirty_for_io(struct pageif (mapping) {if (TestClearPageDirty (page)) { -if (!mapping->backing_dev_info->memory_backed) +if (mapping_cap_account_dirty (mapping))dec_page_state (nr_dirty);return 1 ; }
mm/readahead.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/readahead.c +1 -0
@@ -23,6 +23,7 @@ EXPORT_SYMBOL(default_unplug_io_fn);struct backing_dev_info default_backing_dev_info = { .ra_pages = (VM_MAX_READAHEAD *1024 ) /PAGE_CACHE_SIZE , .state =0 , + .capabilities =BDI_CAP_MAP_COPY , .unplug_io_fn = default_unplug_io_fn, };EXPORT_SYMBOL_GPL (default_backing_dev_info);
mm/shmem.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/shmem.c +2 -2
@@ -184,8 +184,8 @@ static struct vm_operations_struct shmemstatic struct backing_dev_info shmem_backing_dev_info = { .ra_pages =0 ,/* No readahead */ - .memory_backed =1 ,/* Does not contribute to dirty memory */ - .unplug_io_fn = default_unplug_io_fn, + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK , + .unplug_io_fn = default_unplug_io_fn, };static LIST_HEAD (shmem_swaplist);
mm/swap_state.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/swap_state.c +1 -1
@@ -29,7 +29,7 @@ static struct address_space_operations s };static struct backing_dev_info swap_backing_dev_info = { - .memory_backed =1 ,/* Does not contribute to dirty memory */ + .capabilities =BDI_CAP_NO_ACCT_DIRTY |BDI_CAP_NO_WRITEBACK , .unplug_io_fn = swap_unplug_io_fn, };
Re: [PATCH 1/2] BDI: Provide backing device capability information [try #3]
Quoted 3 lines · David Howells
The attached patch replaces
backing_dev_info::memory_backedwith capabilitied bitmap.
Looks sane to me, thanks.
I hope you got all the conversions correct - breakage in the writeback dirty accounting manifests in subtle ways. I'll double-check it.
Re: [PATCH 1/2] BDI: Provide backing device capability information [try #3]
Quoted 7 lines · Andrew Morton
Quoted 2 lines
The attached patch replaces
backing_dev_info::memory_backedwith capabilitied bitmap.Looks sane to me, thanks.
I hope you got all the conversions correct - breakage in the writeback dirty accounting manifests in subtle ways. I'll double-check it.
I think I got the logic as-was. This is quite easy to check just by looking at the patch. However, the as-was logic should possibly be changed to reflect the fact that the one control that there was has now been split into two.
David
PATCH 2/2 BDI: Improve nommu mmap support
In reply to Andrew MortonThe attached patch makes use of patch #1 to improve nommu mmap support; particularly in terms on supporting private mappings. It does this by examining the device capability mask now in the backing_dev_info structure.
Private mappings will now be backed by the underlying device directly if possible, where "possible" is constrained by the protection mask parameter and the device capabilities mask.
I've also split the do_mmap_pgoff() function contents into a number of auxilliary functions to make it easier to understand.
The documentation is also updated; including the addition of a warning about permitting direct mapping of flash chips and the problems of XIP vs write.
---
warthog>diffstat -p1 memback-nommu-2611rc4.diff
Documentation/nommu-mmap.txt | 81 +++++- include/linux/mm.h | 1 mm/nommu.c | 519 ++++++++++++++++++++++++++----------------- 3 files changed, 384 insertions(+), 217 deletions(-)
include/linux/mm.h renamed from warthog/kernels/linux-2.6.11-rc4/include/linux/mm.h +1 -0
@@ -164,6 +164,7 @@ extern unsigned int kobjsize(const void#define VM_ACCOUNT 0x00100000/* Is a VM accounted object */ #define VM_HUGETLB 0x00400000/* Huge TLB Page VM */ #define VM_NONLINEAR 0x00800000/* Is non-linear (remap_file_pages) */ +#define VM_MAPPED_COPY 0x01000000/* T if mapped copy of data (nommu mmap) */ #ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */ #define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
mm/nommu.c renamed from warthog/kernels/linux-2.6.11-rc4/mm/nommu.c +314 -205
653 lines not on this page · raw message
Documentation/nommu-mmap.txt renamed from warthog/kernels/linux-2.6.11-rc4/Documentation/nommu-mmap.txt +69 -12
123 lines not on this page · raw message