diff options
-rw-r--r-- | fs/inode.c | 1 | ||||
-rw-r--r-- | include/linux/fs.h | 29 | ||||
-rw-r--r-- | kernel/fork.c | 2 | ||||
-rw-r--r-- | mm/mmap.c | 30 | ||||
-rw-r--r-- | mm/swap_state.c | 1 |
5 files changed, 54 insertions, 9 deletions
diff --git a/fs/inode.c b/fs/inode.c index 5938f3928944..26753ba7b6d6 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -165,6 +165,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) | |||
165 | mapping->a_ops = &empty_aops; | 165 | mapping->a_ops = &empty_aops; |
166 | mapping->host = inode; | 166 | mapping->host = inode; |
167 | mapping->flags = 0; | 167 | mapping->flags = 0; |
168 | atomic_set(&mapping->i_mmap_writable, 0); | ||
168 | mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); | 169 | mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); |
169 | mapping->private_data = NULL; | 170 | mapping->private_data = NULL; |
170 | mapping->backing_dev_info = &default_backing_dev_info; | 171 | mapping->backing_dev_info = &default_backing_dev_info; |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 1ab6c6913040..f0890e4a7c25 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -387,7 +387,7 @@ struct address_space { | |||
387 | struct inode *host; /* owner: inode, block_device */ | 387 | struct inode *host; /* owner: inode, block_device */ |
388 | struct radix_tree_root page_tree; /* radix tree of all pages */ | 388 | struct radix_tree_root page_tree; /* radix tree of all pages */ |
389 | spinlock_t tree_lock; /* and lock protecting it */ | 389 | spinlock_t tree_lock; /* and lock protecting it */ |
390 | unsigned int i_mmap_writable;/* count VM_SHARED mappings */ | 390 | atomic_t i_mmap_writable;/* count VM_SHARED mappings */ |
391 | struct rb_root i_mmap; /* tree of private and shared mappings */ | 391 | struct rb_root i_mmap; /* tree of private and shared mappings */ |
392 | struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ | 392 | struct list_head i_mmap_nonlinear;/*list VM_NONLINEAR mappings */ |
393 | struct mutex i_mmap_mutex; /* protect tree, count, list */ | 393 | struct mutex i_mmap_mutex; /* protect tree, count, list */ |
@@ -470,10 +470,35 @@ static inline int mapping_mapped(struct address_space *mapping) | |||
470 | * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff | 470 | * Note that i_mmap_writable counts all VM_SHARED vmas: do_mmap_pgoff |
471 | * marks vma as VM_SHARED if it is shared, and the file was opened for | 471 | * marks vma as VM_SHARED if it is shared, and the file was opened for |
472 | * writing i.e. vma may be mprotected writable even if now readonly. | 472 | * writing i.e. vma may be mprotected writable even if now readonly. |
473 | * | ||
474 | * If i_mmap_writable is negative, no new writable mappings are allowed. You | ||
475 | * can only deny writable mappings, if none exists right now. | ||
473 | */ | 476 | */ |
474 | static inline int mapping_writably_mapped(struct address_space *mapping) | 477 | static inline int mapping_writably_mapped(struct address_space *mapping) |
475 | { | 478 | { |
476 | return mapping->i_mmap_writable != 0; | 479 | return atomic_read(&mapping->i_mmap_writable) > 0; |
480 | } | ||
481 | |||
482 | static inline int mapping_map_writable(struct address_space *mapping) | ||
483 | { | ||
484 | return atomic_inc_unless_negative(&mapping->i_mmap_writable) ? | ||
485 | 0 : -EPERM; | ||
486 | } | ||
487 | |||
488 | static inline void mapping_unmap_writable(struct address_space *mapping) | ||
489 | { | ||
490 | atomic_dec(&mapping->i_mmap_writable); | ||
491 | } | ||
492 | |||
493 | static inline int mapping_deny_writable(struct address_space *mapping) | ||
494 | { | ||
495 | return atomic_dec_unless_positive(&mapping->i_mmap_writable) ? | ||
496 | 0 : -EBUSY; | ||
497 | } | ||
498 | |||
499 | static inline void mapping_allow_writable(struct address_space *mapping) | ||
500 | { | ||
501 | atomic_inc(&mapping->i_mmap_writable); | ||
477 | } | 502 | } |
478 | 503 | ||
479 | /* | 504 | /* |
diff --git a/kernel/fork.c b/kernel/fork.c index fa9124322cd4..1380d8ace334 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -429,7 +429,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm) | |||
429 | atomic_dec(&inode->i_writecount); | 429 | atomic_dec(&inode->i_writecount); |
430 | mutex_lock(&mapping->i_mmap_mutex); | 430 | mutex_lock(&mapping->i_mmap_mutex); |
431 | if (tmp->vm_flags & VM_SHARED) | 431 | if (tmp->vm_flags & VM_SHARED) |
432 | mapping->i_mmap_writable++; | 432 | atomic_inc(&mapping->i_mmap_writable); |
433 | flush_dcache_mmap_lock(mapping); | 433 | flush_dcache_mmap_lock(mapping); |
434 | /* insert tmp into the share list, just after mpnt */ | 434 | /* insert tmp into the share list, just after mpnt */ |
435 | if (unlikely(tmp->vm_flags & VM_NONLINEAR)) | 435 | if (unlikely(tmp->vm_flags & VM_NONLINEAR)) |
@@ -221,7 +221,7 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma, | |||
221 | if (vma->vm_flags & VM_DENYWRITE) | 221 | if (vma->vm_flags & VM_DENYWRITE) |
222 | atomic_inc(&file_inode(file)->i_writecount); | 222 | atomic_inc(&file_inode(file)->i_writecount); |
223 | if (vma->vm_flags & VM_SHARED) | 223 | if (vma->vm_flags & VM_SHARED) |
224 | mapping->i_mmap_writable--; | 224 | mapping_unmap_writable(mapping); |
225 | 225 | ||
226 | flush_dcache_mmap_lock(mapping); | 226 | flush_dcache_mmap_lock(mapping); |
227 | if (unlikely(vma->vm_flags & VM_NONLINEAR)) | 227 | if (unlikely(vma->vm_flags & VM_NONLINEAR)) |
@@ -622,7 +622,7 @@ static void __vma_link_file(struct vm_area_struct *vma) | |||
622 | if (vma->vm_flags & VM_DENYWRITE) | 622 | if (vma->vm_flags & VM_DENYWRITE) |
623 | atomic_dec(&file_inode(file)->i_writecount); | 623 | atomic_dec(&file_inode(file)->i_writecount); |
624 | if (vma->vm_flags & VM_SHARED) | 624 | if (vma->vm_flags & VM_SHARED) |
625 | mapping->i_mmap_writable++; | 625 | atomic_inc(&mapping->i_mmap_writable); |
626 | 626 | ||
627 | flush_dcache_mmap_lock(mapping); | 627 | flush_dcache_mmap_lock(mapping); |
628 | if (unlikely(vma->vm_flags & VM_NONLINEAR)) | 628 | if (unlikely(vma->vm_flags & VM_NONLINEAR)) |
@@ -1577,6 +1577,17 @@ munmap_back: | |||
1577 | if (error) | 1577 | if (error) |
1578 | goto free_vma; | 1578 | goto free_vma; |
1579 | } | 1579 | } |
1580 | if (vm_flags & VM_SHARED) { | ||
1581 | error = mapping_map_writable(file->f_mapping); | ||
1582 | if (error) | ||
1583 | goto allow_write_and_free_vma; | ||
1584 | } | ||
1585 | |||
1586 | /* ->mmap() can change vma->vm_file, but must guarantee that | ||
1587 | * vma_link() below can deny write-access if VM_DENYWRITE is set | ||
1588 | * and map writably if VM_SHARED is set. This usually means the | ||
1589 | * new file must not have been exposed to user-space, yet. | ||
1590 | */ | ||
1580 | vma->vm_file = get_file(file); | 1591 | vma->vm_file = get_file(file); |
1581 | error = file->f_op->mmap(file, vma); | 1592 | error = file->f_op->mmap(file, vma); |
1582 | if (error) | 1593 | if (error) |
@@ -1616,8 +1627,12 @@ munmap_back: | |||
1616 | 1627 | ||
1617 | vma_link(mm, vma, prev, rb_link, rb_parent); | 1628 | vma_link(mm, vma, prev, rb_link, rb_parent); |
1618 | /* Once vma denies write, undo our temporary denial count */ | 1629 | /* Once vma denies write, undo our temporary denial count */ |
1619 | if (vm_flags & VM_DENYWRITE) | 1630 | if (file) { |
1620 | allow_write_access(file); | 1631 | if (vm_flags & VM_SHARED) |
1632 | mapping_unmap_writable(file->f_mapping); | ||
1633 | if (vm_flags & VM_DENYWRITE) | ||
1634 | allow_write_access(file); | ||
1635 | } | ||
1621 | file = vma->vm_file; | 1636 | file = vma->vm_file; |
1622 | out: | 1637 | out: |
1623 | perf_event_mmap(vma); | 1638 | perf_event_mmap(vma); |
@@ -1646,14 +1661,17 @@ out: | |||
1646 | return addr; | 1661 | return addr; |
1647 | 1662 | ||
1648 | unmap_and_free_vma: | 1663 | unmap_and_free_vma: |
1649 | if (vm_flags & VM_DENYWRITE) | ||
1650 | allow_write_access(file); | ||
1651 | vma->vm_file = NULL; | 1664 | vma->vm_file = NULL; |
1652 | fput(file); | 1665 | fput(file); |
1653 | 1666 | ||
1654 | /* Undo any partial mapping done by a device driver. */ | 1667 | /* Undo any partial mapping done by a device driver. */ |
1655 | unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); | 1668 | unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end); |
1656 | charged = 0; | 1669 | charged = 0; |
1670 | if (vm_flags & VM_SHARED) | ||
1671 | mapping_unmap_writable(file->f_mapping); | ||
1672 | allow_write_and_free_vma: | ||
1673 | if (vm_flags & VM_DENYWRITE) | ||
1674 | allow_write_access(file); | ||
1657 | free_vma: | 1675 | free_vma: |
1658 | kmem_cache_free(vm_area_cachep, vma); | 1676 | kmem_cache_free(vm_area_cachep, vma); |
1659 | unacct_error: | 1677 | unacct_error: |
diff --git a/mm/swap_state.c b/mm/swap_state.c index e160151da6b8..3e0ec83d000c 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c | |||
@@ -39,6 +39,7 @@ static struct backing_dev_info swap_backing_dev_info = { | |||
39 | struct address_space swapper_spaces[MAX_SWAPFILES] = { | 39 | struct address_space swapper_spaces[MAX_SWAPFILES] = { |
40 | [0 ... MAX_SWAPFILES - 1] = { | 40 | [0 ... MAX_SWAPFILES - 1] = { |
41 | .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), | 41 | .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), |
42 | .i_mmap_writable = ATOMIC_INIT(0), | ||
42 | .a_ops = &swap_aops, | 43 | .a_ops = &swap_aops, |
43 | .backing_dev_info = &swap_backing_dev_info, | 44 | .backing_dev_info = &swap_backing_dev_info, |
44 | } | 45 | } |