diff options
Diffstat (limited to 'mm/vmalloc.c')
-rw-r--r-- | mm/vmalloc.c | 95 |
1 files changed, 55 insertions, 40 deletions
diff --git a/mm/vmalloc.c b/mm/vmalloc.c index a5bbdd3b5d67..2faaa2976447 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c | |||
@@ -765,7 +765,7 @@ struct vmap_block { | |||
765 | spinlock_t lock; | 765 | spinlock_t lock; |
766 | struct vmap_area *va; | 766 | struct vmap_area *va; |
767 | unsigned long free, dirty; | 767 | unsigned long free, dirty; |
768 | DECLARE_BITMAP(dirty_map, VMAP_BBMAP_BITS); | 768 | unsigned long dirty_min, dirty_max; /*< dirty range */ |
769 | struct list_head free_list; | 769 | struct list_head free_list; |
770 | struct rcu_head rcu_head; | 770 | struct rcu_head rcu_head; |
771 | struct list_head purge; | 771 | struct list_head purge; |
@@ -796,13 +796,31 @@ static unsigned long addr_to_vb_idx(unsigned long addr) | |||
796 | return addr; | 796 | return addr; |
797 | } | 797 | } |
798 | 798 | ||
799 | static struct vmap_block *new_vmap_block(gfp_t gfp_mask) | 799 | static void *vmap_block_vaddr(unsigned long va_start, unsigned long pages_off) |
800 | { | ||
801 | unsigned long addr; | ||
802 | |||
803 | addr = va_start + (pages_off << PAGE_SHIFT); | ||
804 | BUG_ON(addr_to_vb_idx(addr) != addr_to_vb_idx(va_start)); | ||
805 | return (void *)addr; | ||
806 | } | ||
807 | |||
808 | /** | ||
809 | * new_vmap_block - allocates new vmap_block and occupies 2^order pages in this | ||
810 | * block. Of course pages number can't exceed VMAP_BBMAP_BITS | ||
811 | * @order: how many 2^order pages should be occupied in newly allocated block | ||
812 | * @gfp_mask: flags for the page level allocator | ||
813 | * | ||
814 | * Returns: virtual address in a newly allocated block or ERR_PTR(-errno) | ||
815 | */ | ||
816 | static void *new_vmap_block(unsigned int order, gfp_t gfp_mask) | ||
800 | { | 817 | { |
801 | struct vmap_block_queue *vbq; | 818 | struct vmap_block_queue *vbq; |
802 | struct vmap_block *vb; | 819 | struct vmap_block *vb; |
803 | struct vmap_area *va; | 820 | struct vmap_area *va; |
804 | unsigned long vb_idx; | 821 | unsigned long vb_idx; |
805 | int node, err; | 822 | int node, err; |
823 | void *vaddr; | ||
806 | 824 | ||
807 | node = numa_node_id(); | 825 | node = numa_node_id(); |
808 | 826 | ||
@@ -826,11 +844,15 @@ static struct vmap_block *new_vmap_block(gfp_t gfp_mask) | |||
826 | return ERR_PTR(err); | 844 | return ERR_PTR(err); |
827 | } | 845 | } |
828 | 846 | ||
847 | vaddr = vmap_block_vaddr(va->va_start, 0); | ||
829 | spin_lock_init(&vb->lock); | 848 | spin_lock_init(&vb->lock); |
830 | vb->va = va; | 849 | vb->va = va; |
831 | vb->free = VMAP_BBMAP_BITS; | 850 | /* At least something should be left free */ |
851 | BUG_ON(VMAP_BBMAP_BITS <= (1UL << order)); | ||
852 | vb->free = VMAP_BBMAP_BITS - (1UL << order); | ||
832 | vb->dirty = 0; | 853 | vb->dirty = 0; |
833 | bitmap_zero(vb->dirty_map, VMAP_BBMAP_BITS); | 854 | vb->dirty_min = VMAP_BBMAP_BITS; |
855 | vb->dirty_max = 0; | ||
834 | INIT_LIST_HEAD(&vb->free_list); | 856 | INIT_LIST_HEAD(&vb->free_list); |
835 | 857 | ||
836 | vb_idx = addr_to_vb_idx(va->va_start); | 858 | vb_idx = addr_to_vb_idx(va->va_start); |
@@ -842,11 +864,11 @@ static struct vmap_block *new_vmap_block(gfp_t gfp_mask) | |||
842 | 864 | ||
843 | vbq = &get_cpu_var(vmap_block_queue); | 865 | vbq = &get_cpu_var(vmap_block_queue); |
844 | spin_lock(&vbq->lock); | 866 | spin_lock(&vbq->lock); |
845 | list_add_rcu(&vb->free_list, &vbq->free); | 867 | list_add_tail_rcu(&vb->free_list, &vbq->free); |
846 | spin_unlock(&vbq->lock); | 868 | spin_unlock(&vbq->lock); |
847 | put_cpu_var(vmap_block_queue); | 869 | put_cpu_var(vmap_block_queue); |
848 | 870 | ||
849 | return vb; | 871 | return vaddr; |
850 | } | 872 | } |
851 | 873 | ||
852 | static void free_vmap_block(struct vmap_block *vb) | 874 | static void free_vmap_block(struct vmap_block *vb) |
@@ -881,7 +903,8 @@ static void purge_fragmented_blocks(int cpu) | |||
881 | if (vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS) { | 903 | if (vb->free + vb->dirty == VMAP_BBMAP_BITS && vb->dirty != VMAP_BBMAP_BITS) { |
882 | vb->free = 0; /* prevent further allocs after releasing lock */ | 904 | vb->free = 0; /* prevent further allocs after releasing lock */ |
883 | vb->dirty = VMAP_BBMAP_BITS; /* prevent purging it again */ | 905 | vb->dirty = VMAP_BBMAP_BITS; /* prevent purging it again */ |
884 | bitmap_fill(vb->dirty_map, VMAP_BBMAP_BITS); | 906 | vb->dirty_min = 0; |
907 | vb->dirty_max = VMAP_BBMAP_BITS; | ||
885 | spin_lock(&vbq->lock); | 908 | spin_lock(&vbq->lock); |
886 | list_del_rcu(&vb->free_list); | 909 | list_del_rcu(&vb->free_list); |
887 | spin_unlock(&vbq->lock); | 910 | spin_unlock(&vbq->lock); |
@@ -910,7 +933,7 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask) | |||
910 | { | 933 | { |
911 | struct vmap_block_queue *vbq; | 934 | struct vmap_block_queue *vbq; |
912 | struct vmap_block *vb; | 935 | struct vmap_block *vb; |
913 | unsigned long addr = 0; | 936 | void *vaddr = NULL; |
914 | unsigned int order; | 937 | unsigned int order; |
915 | 938 | ||
916 | BUG_ON(size & ~PAGE_MASK); | 939 | BUG_ON(size & ~PAGE_MASK); |
@@ -925,43 +948,38 @@ static void *vb_alloc(unsigned long size, gfp_t gfp_mask) | |||
925 | } | 948 | } |
926 | order = get_order(size); | 949 | order = get_order(size); |
927 | 950 | ||
928 | again: | ||
929 | rcu_read_lock(); | 951 | rcu_read_lock(); |
930 | vbq = &get_cpu_var(vmap_block_queue); | 952 | vbq = &get_cpu_var(vmap_block_queue); |
931 | list_for_each_entry_rcu(vb, &vbq->free, free_list) { | 953 | list_for_each_entry_rcu(vb, &vbq->free, free_list) { |
932 | int i; | 954 | unsigned long pages_off; |
933 | 955 | ||
934 | spin_lock(&vb->lock); | 956 | spin_lock(&vb->lock); |
935 | if (vb->free < 1UL << order) | 957 | if (vb->free < (1UL << order)) { |
936 | goto next; | 958 | spin_unlock(&vb->lock); |
959 | continue; | ||
960 | } | ||
937 | 961 | ||
938 | i = VMAP_BBMAP_BITS - vb->free; | 962 | pages_off = VMAP_BBMAP_BITS - vb->free; |
939 | addr = vb->va->va_start + (i << PAGE_SHIFT); | 963 | vaddr = vmap_block_vaddr(vb->va->va_start, pages_off); |
940 | BUG_ON(addr_to_vb_idx(addr) != | ||
941 | addr_to_vb_idx(vb->va->va_start)); | ||
942 | vb->free -= 1UL << order; | 964 | vb->free -= 1UL << order; |
943 | if (vb->free == 0) { | 965 | if (vb->free == 0) { |
944 | spin_lock(&vbq->lock); | 966 | spin_lock(&vbq->lock); |
945 | list_del_rcu(&vb->free_list); | 967 | list_del_rcu(&vb->free_list); |
946 | spin_unlock(&vbq->lock); | 968 | spin_unlock(&vbq->lock); |
947 | } | 969 | } |
970 | |||
948 | spin_unlock(&vb->lock); | 971 | spin_unlock(&vb->lock); |
949 | break; | 972 | break; |
950 | next: | ||
951 | spin_unlock(&vb->lock); | ||
952 | } | 973 | } |
953 | 974 | ||
954 | put_cpu_var(vmap_block_queue); | 975 | put_cpu_var(vmap_block_queue); |
955 | rcu_read_unlock(); | 976 | rcu_read_unlock(); |
956 | 977 | ||
957 | if (!addr) { | 978 | /* Allocate new block if nothing was found */ |
958 | vb = new_vmap_block(gfp_mask); | 979 | if (!vaddr) |
959 | if (IS_ERR(vb)) | 980 | vaddr = new_vmap_block(order, gfp_mask); |
960 | return vb; | ||
961 | goto again; | ||
962 | } | ||
963 | 981 | ||
964 | return (void *)addr; | 982 | return vaddr; |
965 | } | 983 | } |
966 | 984 | ||
967 | static void vb_free(const void *addr, unsigned long size) | 985 | static void vb_free(const void *addr, unsigned long size) |
@@ -979,6 +997,7 @@ static void vb_free(const void *addr, unsigned long size) | |||
979 | order = get_order(size); | 997 | order = get_order(size); |
980 | 998 | ||
981 | offset = (unsigned long)addr & (VMAP_BLOCK_SIZE - 1); | 999 | offset = (unsigned long)addr & (VMAP_BLOCK_SIZE - 1); |
1000 | offset >>= PAGE_SHIFT; | ||
982 | 1001 | ||
983 | vb_idx = addr_to_vb_idx((unsigned long)addr); | 1002 | vb_idx = addr_to_vb_idx((unsigned long)addr); |
984 | rcu_read_lock(); | 1003 | rcu_read_lock(); |
@@ -989,7 +1008,10 @@ static void vb_free(const void *addr, unsigned long size) | |||
989 | vunmap_page_range((unsigned long)addr, (unsigned long)addr + size); | 1008 | vunmap_page_range((unsigned long)addr, (unsigned long)addr + size); |
990 | 1009 | ||
991 | spin_lock(&vb->lock); | 1010 | spin_lock(&vb->lock); |
992 | BUG_ON(bitmap_allocate_region(vb->dirty_map, offset >> PAGE_SHIFT, order)); | 1011 | |
1012 | /* Expand dirty range */ | ||
1013 | vb->dirty_min = min(vb->dirty_min, offset); | ||
1014 | vb->dirty_max = max(vb->dirty_max, offset + (1UL << order)); | ||
993 | 1015 | ||
994 | vb->dirty += 1UL << order; | 1016 | vb->dirty += 1UL << order; |
995 | if (vb->dirty == VMAP_BBMAP_BITS) { | 1017 | if (vb->dirty == VMAP_BBMAP_BITS) { |
@@ -1028,25 +1050,18 @@ void vm_unmap_aliases(void) | |||
1028 | 1050 | ||
1029 | rcu_read_lock(); | 1051 | rcu_read_lock(); |
1030 | list_for_each_entry_rcu(vb, &vbq->free, free_list) { | 1052 | list_for_each_entry_rcu(vb, &vbq->free, free_list) { |
1031 | int i, j; | ||
1032 | |||
1033 | spin_lock(&vb->lock); | 1053 | spin_lock(&vb->lock); |
1034 | i = find_first_bit(vb->dirty_map, VMAP_BBMAP_BITS); | 1054 | if (vb->dirty) { |
1035 | if (i < VMAP_BBMAP_BITS) { | 1055 | unsigned long va_start = vb->va->va_start; |
1036 | unsigned long s, e; | 1056 | unsigned long s, e; |
1037 | 1057 | ||
1038 | j = find_last_bit(vb->dirty_map, | 1058 | s = va_start + (vb->dirty_min << PAGE_SHIFT); |
1039 | VMAP_BBMAP_BITS); | 1059 | e = va_start + (vb->dirty_max << PAGE_SHIFT); |
1040 | j = j + 1; /* need exclusive index */ | ||
1041 | 1060 | ||
1042 | s = vb->va->va_start + (i << PAGE_SHIFT); | 1061 | start = min(s, start); |
1043 | e = vb->va->va_start + (j << PAGE_SHIFT); | 1062 | end = max(e, end); |
1044 | flush = 1; | ||
1045 | 1063 | ||
1046 | if (s < start) | 1064 | flush = 1; |
1047 | start = s; | ||
1048 | if (e > end) | ||
1049 | end = e; | ||
1050 | } | 1065 | } |
1051 | spin_unlock(&vb->lock); | 1066 | spin_unlock(&vb->lock); |
1052 | } | 1067 | } |