diff options
Diffstat (limited to 'mm/vmalloc.c')
-rw-r--r-- | mm/vmalloc.c | 114 |
1 files changed, 87 insertions, 27 deletions
diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 37e69295f250..ae007462b7f6 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c | |||
@@ -509,6 +509,9 @@ static unsigned long lazy_max_pages(void) | |||
509 | 509 | ||
510 | static atomic_t vmap_lazy_nr = ATOMIC_INIT(0); | 510 | static atomic_t vmap_lazy_nr = ATOMIC_INIT(0); |
511 | 511 | ||
512 | /* for per-CPU blocks */ | ||
513 | static void purge_fragmented_blocks_allcpus(void); | ||
514 | |||
512 | /* | 515 | /* |
513 | * Purges all lazily-freed vmap areas. | 516 | * Purges all lazily-freed vmap areas. |
514 | * | 517 | * |
@@ -539,6 +542,9 @@ static void __purge_vmap_area_lazy(unsigned long *start, unsigned long *end, | |||
539 | } else | 542 | } else |
540 | spin_lock(&purge_lock); | 543 | spin_lock(&purge_lock); |
541 | 544 | ||
545 | if (sync) | ||
546 | purge_fragmented_blocks_allcpus(); | ||
547 | |||
542 | rcu_read_lock(); | 548 | rcu_read_lock(); |
543 | list_for_each_entry_rcu(va, &vmap_area_list, list) { | 549 | list_for_each_entry_rcu(va, &vmap_area_list, list) { |
544 | if (va->flags & VM_LAZY_FREE) { | 550 | if (va->flags & VM_LAZY_FREE) { |
@@ -555,10 +561,8 @@ static void __purge_vmap_area_lazy(unsigned long *start, unsigned long *end, | |||
555 | } | 561 | } |
556 | rcu_read_unlock(); | 562 | rcu_read_unlock(); |
557 | 563 | ||
558 | if (nr) { | 564 | if (nr) |
559 | BUG_ON(nr > atomic_read(&vmap_lazy_nr)); | ||
560 | atomic_sub(nr, &vmap_lazy_nr); | 565 | atomic_sub(nr, &vmap_lazy_nr); |
561 | } | ||
562 | 566 | ||
563 | if (nr || force_flush) | 567 | if (nr || force_flush) |
564 | flush_tlb_kernel_range(*start, *end); | 568 | flush_tlb_kernel_range(*start, *end); |
@@ -669,8 +673,6 @@ static bool vmap_initialized __read_mostly = false; | |||
669 | struct vmap_block_queue { | 673 | struct vmap_block_queue { |
670 | spinlock_t lock; | 674 | spinlock_t lock; |
671 | struct list_head free; | 675 | struct list_head free; |
672 | struct list_head dirty; | ||
673 | unsigned int nr_dirty; | ||
674 | }; | 676 | }; |
675 | 677 | ||
676 | struct vmap_block { | 678 | struct vmap_block { |
@@ -680,10 +682,9 @@ struct vmap_block { | |||
680 | unsigned long free, dirty; | 682 | unsigned long free, dirty; |
681 | DECLARE_BITMAP(alloc_map, VMAP_BBMAP_BITS); | 683 | DECLARE_BITMAP(alloc_map, VMAP_BBMAP_BITS); |
682 | DECLARE_BITMAP(dirty_map, VMAP_BBMAP_BITS); | 684 | DECLARE_BITMAP(dirty_map, VMAP_BBMAP_BITS); |
683 | union { | 685 | struct list_head free_list; |
684 | struct list_head free_list; | 686 | struct rcu_head rcu_head; |
685 | struct rcu_head rcu_head; | 687 | struct list_head purge; |
686 | }; | ||
687 | }; | 688 | }; |
688 | 689 | ||
689 | /* Queue of free and dirty vmap blocks, for allocation and flushing purposes */ | 690 | /* Queue of free and dirty vmap blocks, for allocation and flushing purposes */ |
@@ -759,7 +760,7 @@ static struct vmap_block *new_vmap_block(gfp_t gfp_mask) | |||
759 | vbq = &get_cpu_var(vmap_block_queue); | 760 | vbq = &get_cpu_var(vmap_block_queue); |
760 | vb->vbq = vbq; | 761 | vb->vbq = vbq; |
761 | spin_lock(&vbq->lock); | 762 | spin_lock(&vbq->lock); |
762 | list_add(&vb->free_list, &vbq->free); | 763 | list_add_rcu(&vb->free_list, &vbq->free); |
763 | spin_unlock(&vbq->lock); | 764 | spin_unlock(&vbq->lock); |
764 | put_cpu_var(vmap_block_queue); | 765 | put_cpu_var(vmap_block_queue); |
765 | 766 | ||
@@ -778,8 +779,6 @@ static void free_vmap_block(struct vmap_block *vb) | |||
778 | struct vmap_block *tmp; | 779 | struct vmap_block *tmp; |
779 | unsigned long vb_idx; | 780 | unsigned long vb_idx; |
780 | 781 | ||
781 | BUG_ON(!list_empty(&vb->free_list)); | ||
782 | |||
783 | vb_idx = addr_to_vb_idx(vb->va->va_start); | 782 | vb_idx = addr_to_vb_idx(vb->va->va_start); |
784 | spin_lock(&vmap_block_tree_lock); | 783 | spin_lock(&vmap_block_tree_lock); |
785 | tmp = radix_tree_delete(&vmap_block_tree, vb_idx); | 784 | tmp = radix_tree_delete(&vmap_block_tree, vb_idx); |
@@ -790,12 +789,61 @@ static void free_vmap_block(struct vmap_block *vb) | |||
790 | call_rcu(&vb->rcu_head, rcu_free_vb); | 789 | call_rcu(&vb->rcu_head, rcu_free_vb); |
791 | } | 790 | } |
792 | 791 | ||
792 | static void purge_fragmented_blocks(int cpu) | ||
793 | { | ||
794 | LIST_HEAD(purge); | ||
795 | struct vmap_block *vb; | ||
796 | struct vmap_block *n_vb; | ||
797 | struct vmap_block_queue *vbq = &per_cpu(vmap_block_queue, cpu); | ||
798 | |||
799 | rcu_read_lock(); | ||
800 | list_for_each_entry_rcu(vb, &vbq->free, free_list) { | ||
801 | |||
802 | if (!(vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS)) | ||
803 | continue; | ||
804 | |||
805 | spin_lock(&vb->lock); | ||
806 | if (vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS) { | ||
807 | vb->free = 0; /* prevent further allocs after releasing lock */ | ||
808 | vb->dirty = VMAP_BBMAP_BITS; /* prevent purging it again */ | ||
809 | bitmap_fill(vb->alloc_map, VMAP_BBMAP_BITS); | ||
810 | bitmap_fill(vb->dirty_map, VMAP_BBMAP_BITS); | ||
811 | spin_lock(&vbq->lock); | ||
812 | list_del_rcu(&vb->free_list); | ||
813 | spin_unlock(&vbq->lock); | ||
814 | spin_unlock(&vb->lock); | ||
815 | list_add_tail(&vb->purge, &purge); | ||
816 | } else | ||
817 | spin_unlock(&vb->lock); | ||
818 | } | ||
819 | rcu_read_unlock(); | ||
820 | |||
821 | list_for_each_entry_safe(vb, n_vb, &purge, purge) { | ||
822 | list_del(&vb->purge); | ||
823 | free_vmap_block(vb); | ||
824 | } | ||
825 | } | ||
826 | |||
827 | static void purge_fragmented_blocks_thiscpu(void) | ||
828 | { | ||
829 | purge_fragmented_blocks(smp_processor_id()); | ||
830 | } | ||
831 | |||
832 | static void purge_fragmented_blocks_allcpus(void) | ||
833 | { | ||
834 | int cpu; | ||
835 | |||
836 | for_each_possible_cpu(cpu) | ||
837 | purge_fragmented_blocks(cpu); | ||
838 | } | ||
839 | |||
793 | static void *vb_alloc(unsigned long size, gfp_t gfp_mask) | 840 | static void *vb_alloc(unsigned long size, gfp_t gfp_mask) |
794 | { | 841 | { |
795 | struct vmap_block_queue *vbq; | 842 | struct vmap_block_queue *vbq; |
796 | struct vmap_block *vb; | 843 | struct vmap_block *vb; |
797 | unsigned long addr = 0; | 844 | unsigned long addr = 0; |
798 | unsigned int order; | 845 | unsigned int order; |
846 | int purge = 0; | ||
799 | 847 | ||
800 | BUG_ON(size & ~PAGE_MASK); | 848 | BUG_ON(size & ~PAGE_MASK); |
801 | BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC); | 849 | BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC); |
@@ -808,24 +856,38 @@ again: | |||
808 | int i; | 856 | int i; |
809 | 857 | ||
810 | spin_lock(&vb->lock); | 858 | spin_lock(&vb->lock); |
859 | if (vb->free < 1UL << order) | ||
860 | goto next; | ||
861 | |||
811 | i = bitmap_find_free_region(vb->alloc_map, | 862 | i = bitmap_find_free_region(vb->alloc_map, |
812 | VMAP_BBMAP_BITS, order); | 863 | VMAP_BBMAP_BITS, order); |
813 | 864 | ||
814 | if (i >= 0) { | 865 | if (i < 0) { |
815 | addr = vb->va->va_start + (i << PAGE_SHIFT); | 866 | if (vb->free + vb->dirty == VMAP_BBMAP_BITS) { |
816 | BUG_ON(addr_to_vb_idx(addr) != | 867 | /* fragmented and no outstanding allocations */ |
817 | addr_to_vb_idx(vb->va->va_start)); | 868 | BUG_ON(vb->dirty != VMAP_BBMAP_BITS); |
818 | vb->free -= 1UL << order; | 869 | purge = 1; |
819 | if (vb->free == 0) { | ||
820 | spin_lock(&vbq->lock); | ||
821 | list_del_init(&vb->free_list); | ||
822 | spin_unlock(&vbq->lock); | ||
823 | } | 870 | } |
824 | spin_unlock(&vb->lock); | 871 | goto next; |
825 | break; | 872 | } |
873 | addr = vb->va->va_start + (i << PAGE_SHIFT); | ||
874 | BUG_ON(addr_to_vb_idx(addr) != | ||
875 | addr_to_vb_idx(vb->va->va_start)); | ||
876 | vb->free -= 1UL << order; | ||
877 | if (vb->free == 0) { | ||
878 | spin_lock(&vbq->lock); | ||
879 | list_del_rcu(&vb->free_list); | ||
880 | spin_unlock(&vbq->lock); | ||
826 | } | 881 | } |
827 | spin_unlock(&vb->lock); | 882 | spin_unlock(&vb->lock); |
883 | break; | ||
884 | next: | ||
885 | spin_unlock(&vb->lock); | ||
828 | } | 886 | } |
887 | |||
888 | if (purge) | ||
889 | purge_fragmented_blocks_thiscpu(); | ||
890 | |||
829 | put_cpu_var(vmap_block_queue); | 891 | put_cpu_var(vmap_block_queue); |
830 | rcu_read_unlock(); | 892 | rcu_read_unlock(); |
831 | 893 | ||
@@ -862,11 +924,11 @@ static void vb_free(const void *addr, unsigned long size) | |||
862 | BUG_ON(!vb); | 924 | BUG_ON(!vb); |
863 | 925 | ||
864 | spin_lock(&vb->lock); | 926 | spin_lock(&vb->lock); |
865 | bitmap_allocate_region(vb->dirty_map, offset >> PAGE_SHIFT, order); | 927 | BUG_ON(bitmap_allocate_region(vb->dirty_map, offset >> PAGE_SHIFT, order)); |
866 | 928 | ||
867 | vb->dirty += 1UL << order; | 929 | vb->dirty += 1UL << order; |
868 | if (vb->dirty == VMAP_BBMAP_BITS) { | 930 | if (vb->dirty == VMAP_BBMAP_BITS) { |
869 | BUG_ON(vb->free || !list_empty(&vb->free_list)); | 931 | BUG_ON(vb->free); |
870 | spin_unlock(&vb->lock); | 932 | spin_unlock(&vb->lock); |
871 | free_vmap_block(vb); | 933 | free_vmap_block(vb); |
872 | } else | 934 | } else |
@@ -1035,8 +1097,6 @@ void __init vmalloc_init(void) | |||
1035 | vbq = &per_cpu(vmap_block_queue, i); | 1097 | vbq = &per_cpu(vmap_block_queue, i); |
1036 | spin_lock_init(&vbq->lock); | 1098 | spin_lock_init(&vbq->lock); |
1037 | INIT_LIST_HEAD(&vbq->free); | 1099 | INIT_LIST_HEAD(&vbq->free); |
1038 | INIT_LIST_HEAD(&vbq->dirty); | ||
1039 | vbq->nr_dirty = 0; | ||
1040 | } | 1100 | } |
1041 | 1101 | ||
1042 | /* Import existing vmlist entries. */ | 1102 | /* Import existing vmlist entries. */ |