diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /mm/filemap.c | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'mm/filemap.c')
-rw-r--r-- | mm/filemap.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/mm/filemap.c b/mm/filemap.c new file mode 100644 index 000000000..5a1e99142 --- /dev/null +++ b/mm/filemap.c @@ -0,0 +1,274 @@ +/* + * linux/mm/filemmap.c + * + * Copyright (C) 1994 Linus Torvalds + */ + +/* + * This file handles the generic file mmap semantics used by + * most "normal" filesystems (but you don't /have/ to use this: + * the NFS filesystem does this differently, for example) + */ +#include <linux/stat.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/shm.h> +#include <linux/errno.h> +#include <linux/mman.h> +#include <linux/string.h> +#include <linux/malloc.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/pgtable.h> + +/* + * Shared mappings implemented 30.11.1994. It's not fully working yet, + * though. + */ + +static unsigned long filemap_nopage(struct vm_area_struct * area, unsigned long address, + unsigned long page, int no_share) +{ + struct inode * inode = area->vm_inode; + unsigned int block; + int nr[8]; + int i, *p; + + address &= PAGE_MASK; + block = address - area->vm_start + area->vm_offset; + block >>= inode->i_sb->s_blocksize_bits; + i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits; + p = nr; + do { + *p = bmap(inode,block); + i--; + block++; + p++; + } while (i > 0); + return bread_page(page, inode->i_dev, nr, inode->i_sb->s_blocksize, no_share); +} + +/* + * NOTE! mmap sync doesn't really work yet. This is mainly a stub for it, + * which only works if the buffers and the page were already sharing the + * same physical page (that's actually pretty common, especially if the + * file has been mmap'ed before being read the normal way). + * + * Todo: + * - non-shared pages also need to be synced with the buffers. + * - the "swapout()" function needs to swap out the page to + * the shared file instead of using the swap device. + */ +static void filemap_sync_page(struct vm_area_struct * vma, + unsigned long offset, + unsigned long page) +{ + struct buffer_head * bh; + + printk("msync: %ld: [%08lx]\n", offset, page); + bh = buffer_pages[MAP_NR(page)]; + if (bh) { + /* whee.. just mark the buffer heads dirty */ + struct buffer_head * tmp = bh; + do { + mark_buffer_dirty(tmp, 0); + tmp = tmp->b_this_page; + } while (tmp != bh); + return; + } + /* we'll need to go fetch the buffer heads etc.. RSN */ + printk("Can't handle non-shared page yet\n"); + return; +} + +static inline void filemap_sync_pte(pte_t * pte, struct vm_area_struct *vma, + unsigned long address, unsigned int flags) +{ + pte_t page = *pte; + + if (!pte_present(page)) + return; + if (!pte_dirty(page)) + return; + if (flags & MS_INVALIDATE) { + pte_clear(pte); + } else { + mem_map[MAP_NR(pte_page(page))]++; + *pte = pte_mkclean(page); + } + filemap_sync_page(vma, address - vma->vm_start, pte_page(page)); + free_page(pte_page(page)); +} + +static inline void filemap_sync_pte_range(pmd_t * pmd, + unsigned long address, unsigned long size, + struct vm_area_struct *vma, unsigned long offset, unsigned int flags) +{ + pte_t * pte; + unsigned long end; + + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + printk("filemap_sync_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_clear(pmd); + return; + } + pte = pte_offset(pmd, address); + offset += address & PMD_MASK; + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + filemap_sync_pte(pte, vma, address + offset, flags); + address += PAGE_SIZE; + pte++; + } while (address < end); +} + +static inline void filemap_sync_pmd_range(pgd_t * pgd, + unsigned long address, unsigned long size, + struct vm_area_struct *vma, unsigned int flags) +{ + pmd_t * pmd; + unsigned long offset, end; + + if (pgd_none(*pgd)) + return; + if (pgd_bad(*pgd)) { + printk("filemap_sync_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_clear(pgd); + return; + } + pmd = pmd_offset(pgd, address); + offset = address & PMD_MASK; + address &= ~PMD_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + filemap_sync_pte_range(pmd, address, end - address, vma, offset, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void filemap_sync(struct vm_area_struct * vma, unsigned long address, + size_t size, unsigned int flags) +{ + pgd_t * dir; + unsigned long end = address + size; + + dir = pgd_offset(current, address); + while (address < end) { + filemap_sync_pmd_range(dir, address, end - address, vma, flags); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } + invalidate(); + return; +} + +/* + * This handles area unmaps.. + */ +static void filemap_unmap(struct vm_area_struct *vma, unsigned long start, size_t len) +{ + filemap_sync(vma, start, len, MS_ASYNC); +} + +/* + * This handles complete area closes.. + */ +static void filemap_close(struct vm_area_struct * vma) +{ + filemap_sync(vma, vma->vm_start, vma->vm_end - vma->vm_start, MS_ASYNC); +} + +/* + * This isn't implemented yet: you'll get a warning and incorrect behaviour. + * + * Note that the page is free'd by the higher-level after return, + * so we have to either write it out or just forget it. We currently + * forget it.. + */ +void filemap_swapout(struct vm_area_struct * vma, + unsigned long offset, + pte_t *page_table) +{ + printk("swapout not implemented on shared files..\n"); + pte_clear(page_table); +} + +/* + * Shared mappings need to be able to do the right thing at + * close/unmap/sync. They will also use the private file as + * backing-store for swapping.. + */ +static struct vm_operations_struct file_shared_mmap = { + NULL, /* open */ + filemap_close, /* close */ + filemap_unmap, /* unmap */ + NULL, /* protect */ + filemap_sync, /* sync */ + NULL, /* advise */ + filemap_nopage, /* nopage */ + NULL, /* wppage */ + filemap_swapout, /* swapout */ + NULL, /* swapin */ +}; + +/* + * Private mappings just need to be able to load in the map + * + * (this is actually used for shared mappings as well, if we + * know they can't ever get write permissions..) + */ +static struct vm_operations_struct file_private_mmap = { + NULL, /* open */ + NULL, /* close */ + NULL, /* unmap */ + NULL, /* protect */ + NULL, /* sync */ + NULL, /* advise */ + filemap_nopage, /* nopage */ + NULL, /* wppage */ + NULL, /* swapout */ + NULL, /* swapin */ +}; + +/* This is used for a general mmap of a disk file */ +int generic_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma) +{ + struct vm_operations_struct * ops; + + if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) + return -EINVAL; + if (!inode->i_sb || !S_ISREG(inode->i_mode)) + return -EACCES; + if (!inode->i_op || !inode->i_op->bmap) + return -ENOEXEC; + ops = &file_private_mmap; + if (vma->vm_flags & VM_SHARED) { + if (vma->vm_flags & (VM_WRITE | VM_MAYWRITE)) { + static int nr = 0; + ops = &file_shared_mmap; +#ifndef SHARED_MMAP_REALLY_WORKS /* it doesn't, yet */ + if (nr++ < 5) + printk("%s tried to do a shared writeable mapping\n", current->comm); + return -EINVAL; +#endif + } + } + if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + inode->i_dirt = 1; + } + vma->vm_inode = inode; + inode->i_count++; + vma->vm_ops = ops; + return 0; +} |