diff options
Diffstat (limited to 'mm/zsmalloc.c')
-rw-r--r-- | mm/zsmalloc.c | 180 |
1 files changed, 134 insertions, 46 deletions
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 839a48c3ca27..4d0a063145ec 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c | |||
@@ -155,8 +155,6 @@ | |||
155 | * (reason above) | 155 | * (reason above) |
156 | */ | 156 | */ |
157 | #define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> 8) | 157 | #define ZS_SIZE_CLASS_DELTA (PAGE_SIZE >> 8) |
158 | #define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \ | ||
159 | ZS_SIZE_CLASS_DELTA + 1) | ||
160 | 158 | ||
161 | /* | 159 | /* |
162 | * We do not maintain any list for completely empty or full pages | 160 | * We do not maintain any list for completely empty or full pages |
@@ -171,6 +169,11 @@ enum fullness_group { | |||
171 | }; | 169 | }; |
172 | 170 | ||
173 | /* | 171 | /* |
172 | * number of size_classes | ||
173 | */ | ||
174 | static int zs_size_classes; | ||
175 | |||
176 | /* | ||
174 | * We assign a page to ZS_ALMOST_EMPTY fullness group when: | 177 | * We assign a page to ZS_ALMOST_EMPTY fullness group when: |
175 | * n <= N / f, where | 178 | * n <= N / f, where |
176 | * n = number of allocated objects | 179 | * n = number of allocated objects |
@@ -214,7 +217,7 @@ struct link_free { | |||
214 | }; | 217 | }; |
215 | 218 | ||
216 | struct zs_pool { | 219 | struct zs_pool { |
217 | struct size_class size_class[ZS_SIZE_CLASSES]; | 220 | struct size_class **size_class; |
218 | 221 | ||
219 | gfp_t flags; /* allocation flags used when growing pool */ | 222 | gfp_t flags; /* allocation flags used when growing pool */ |
220 | atomic_long_t pages_allocated; | 223 | atomic_long_t pages_allocated; |
@@ -468,7 +471,7 @@ static enum fullness_group fix_fullness_group(struct zs_pool *pool, | |||
468 | if (newfg == currfg) | 471 | if (newfg == currfg) |
469 | goto out; | 472 | goto out; |
470 | 473 | ||
471 | class = &pool->size_class[class_idx]; | 474 | class = pool->size_class[class_idx]; |
472 | remove_zspage(page, class, currfg); | 475 | remove_zspage(page, class, currfg); |
473 | insert_zspage(page, class, newfg); | 476 | insert_zspage(page, class, newfg); |
474 | set_zspage_mapping(page, class_idx, newfg); | 477 | set_zspage_mapping(page, class_idx, newfg); |
@@ -629,6 +632,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) | |||
629 | struct page *next_page; | 632 | struct page *next_page; |
630 | struct link_free *link; | 633 | struct link_free *link; |
631 | unsigned int i = 1; | 634 | unsigned int i = 1; |
635 | void *vaddr; | ||
632 | 636 | ||
633 | /* | 637 | /* |
634 | * page->index stores offset of first object starting | 638 | * page->index stores offset of first object starting |
@@ -639,8 +643,8 @@ static void init_zspage(struct page *first_page, struct size_class *class) | |||
639 | if (page != first_page) | 643 | if (page != first_page) |
640 | page->index = off; | 644 | page->index = off; |
641 | 645 | ||
642 | link = (struct link_free *)kmap_atomic(page) + | 646 | vaddr = kmap_atomic(page); |
643 | off / sizeof(*link); | 647 | link = (struct link_free *)vaddr + off / sizeof(*link); |
644 | 648 | ||
645 | while ((off += class->size) < PAGE_SIZE) { | 649 | while ((off += class->size) < PAGE_SIZE) { |
646 | link->next = obj_location_to_handle(page, i++); | 650 | link->next = obj_location_to_handle(page, i++); |
@@ -654,7 +658,7 @@ static void init_zspage(struct page *first_page, struct size_class *class) | |||
654 | */ | 658 | */ |
655 | next_page = get_next_page(page); | 659 | next_page = get_next_page(page); |
656 | link->next = obj_location_to_handle(next_page, 0); | 660 | link->next = obj_location_to_handle(next_page, 0); |
657 | kunmap_atomic(link); | 661 | kunmap_atomic(vaddr); |
658 | page = next_page; | 662 | page = next_page; |
659 | off %= PAGE_SIZE; | 663 | off %= PAGE_SIZE; |
660 | } | 664 | } |
@@ -784,7 +788,7 @@ static inline int __zs_cpu_up(struct mapping_area *area) | |||
784 | */ | 788 | */ |
785 | if (area->vm_buf) | 789 | if (area->vm_buf) |
786 | return 0; | 790 | return 0; |
787 | area->vm_buf = (char *)__get_free_page(GFP_KERNEL); | 791 | area->vm_buf = kmalloc(ZS_MAX_ALLOC_SIZE, GFP_KERNEL); |
788 | if (!area->vm_buf) | 792 | if (!area->vm_buf) |
789 | return -ENOMEM; | 793 | return -ENOMEM; |
790 | return 0; | 794 | return 0; |
@@ -792,8 +796,7 @@ static inline int __zs_cpu_up(struct mapping_area *area) | |||
792 | 796 | ||
793 | static inline void __zs_cpu_down(struct mapping_area *area) | 797 | static inline void __zs_cpu_down(struct mapping_area *area) |
794 | { | 798 | { |
795 | if (area->vm_buf) | 799 | kfree(area->vm_buf); |
796 | free_page((unsigned long)area->vm_buf); | ||
797 | area->vm_buf = NULL; | 800 | area->vm_buf = NULL; |
798 | } | 801 | } |
799 | 802 | ||
@@ -881,14 +884,10 @@ static struct notifier_block zs_cpu_nb = { | |||
881 | .notifier_call = zs_cpu_notifier | 884 | .notifier_call = zs_cpu_notifier |
882 | }; | 885 | }; |
883 | 886 | ||
884 | static void zs_exit(void) | 887 | static void zs_unregister_cpu_notifier(void) |
885 | { | 888 | { |
886 | int cpu; | 889 | int cpu; |
887 | 890 | ||
888 | #ifdef CONFIG_ZPOOL | ||
889 | zpool_unregister_driver(&zs_zpool_driver); | ||
890 | #endif | ||
891 | |||
892 | cpu_notifier_register_begin(); | 891 | cpu_notifier_register_begin(); |
893 | 892 | ||
894 | for_each_online_cpu(cpu) | 893 | for_each_online_cpu(cpu) |
@@ -898,31 +897,74 @@ static void zs_exit(void) | |||
898 | cpu_notifier_register_done(); | 897 | cpu_notifier_register_done(); |
899 | } | 898 | } |
900 | 899 | ||
901 | static int zs_init(void) | 900 | static int zs_register_cpu_notifier(void) |
902 | { | 901 | { |
903 | int cpu, ret; | 902 | int cpu, uninitialized_var(ret); |
904 | 903 | ||
905 | cpu_notifier_register_begin(); | 904 | cpu_notifier_register_begin(); |
906 | 905 | ||
907 | __register_cpu_notifier(&zs_cpu_nb); | 906 | __register_cpu_notifier(&zs_cpu_nb); |
908 | for_each_online_cpu(cpu) { | 907 | for_each_online_cpu(cpu) { |
909 | ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); | 908 | ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu); |
910 | if (notifier_to_errno(ret)) { | 909 | if (notifier_to_errno(ret)) |
911 | cpu_notifier_register_done(); | 910 | break; |
912 | goto fail; | ||
913 | } | ||
914 | } | 911 | } |
915 | 912 | ||
916 | cpu_notifier_register_done(); | 913 | cpu_notifier_register_done(); |
914 | return notifier_to_errno(ret); | ||
915 | } | ||
916 | |||
917 | static void init_zs_size_classes(void) | ||
918 | { | ||
919 | int nr; | ||
917 | 920 | ||
921 | nr = (ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / ZS_SIZE_CLASS_DELTA + 1; | ||
922 | if ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) % ZS_SIZE_CLASS_DELTA) | ||
923 | nr += 1; | ||
924 | |||
925 | zs_size_classes = nr; | ||
926 | } | ||
927 | |||
928 | static void __exit zs_exit(void) | ||
929 | { | ||
918 | #ifdef CONFIG_ZPOOL | 930 | #ifdef CONFIG_ZPOOL |
919 | zpool_register_driver(&zs_zpool_driver); | 931 | zpool_unregister_driver(&zs_zpool_driver); |
920 | #endif | 932 | #endif |
933 | zs_unregister_cpu_notifier(); | ||
934 | } | ||
921 | 935 | ||
936 | static int __init zs_init(void) | ||
937 | { | ||
938 | int ret = zs_register_cpu_notifier(); | ||
939 | |||
940 | if (ret) { | ||
941 | zs_unregister_cpu_notifier(); | ||
942 | return ret; | ||
943 | } | ||
944 | |||
945 | init_zs_size_classes(); | ||
946 | |||
947 | #ifdef CONFIG_ZPOOL | ||
948 | zpool_register_driver(&zs_zpool_driver); | ||
949 | #endif | ||
922 | return 0; | 950 | return 0; |
923 | fail: | 951 | } |
924 | zs_exit(); | 952 | |
925 | return notifier_to_errno(ret); | 953 | static unsigned int get_maxobj_per_zspage(int size, int pages_per_zspage) |
954 | { | ||
955 | return pages_per_zspage * PAGE_SIZE / size; | ||
956 | } | ||
957 | |||
958 | static bool can_merge(struct size_class *prev, int size, int pages_per_zspage) | ||
959 | { | ||
960 | if (prev->pages_per_zspage != pages_per_zspage) | ||
961 | return false; | ||
962 | |||
963 | if (get_maxobj_per_zspage(prev->size, prev->pages_per_zspage) | ||
964 | != get_maxobj_per_zspage(size, pages_per_zspage)) | ||
965 | return false; | ||
966 | |||
967 | return true; | ||
926 | } | 968 | } |
927 | 969 | ||
928 | /** | 970 | /** |
@@ -937,33 +979,71 @@ fail: | |||
937 | */ | 979 | */ |
938 | struct zs_pool *zs_create_pool(gfp_t flags) | 980 | struct zs_pool *zs_create_pool(gfp_t flags) |
939 | { | 981 | { |
940 | int i, ovhd_size; | 982 | int i; |
941 | struct zs_pool *pool; | 983 | struct zs_pool *pool; |
984 | struct size_class *prev_class = NULL; | ||
942 | 985 | ||
943 | ovhd_size = roundup(sizeof(*pool), PAGE_SIZE); | 986 | pool = kzalloc(sizeof(*pool), GFP_KERNEL); |
944 | pool = kzalloc(ovhd_size, GFP_KERNEL); | ||
945 | if (!pool) | 987 | if (!pool) |
946 | return NULL; | 988 | return NULL; |
947 | 989 | ||
948 | for (i = 0; i < ZS_SIZE_CLASSES; i++) { | 990 | pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *), |
991 | GFP_KERNEL); | ||
992 | if (!pool->size_class) { | ||
993 | kfree(pool); | ||
994 | return NULL; | ||
995 | } | ||
996 | |||
997 | /* | ||
998 | * Iterate reversly, because, size of size_class that we want to use | ||
999 | * for merging should be larger or equal to current size. | ||
1000 | */ | ||
1001 | for (i = zs_size_classes - 1; i >= 0; i--) { | ||
949 | int size; | 1002 | int size; |
1003 | int pages_per_zspage; | ||
950 | struct size_class *class; | 1004 | struct size_class *class; |
951 | 1005 | ||
952 | size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; | 1006 | size = ZS_MIN_ALLOC_SIZE + i * ZS_SIZE_CLASS_DELTA; |
953 | if (size > ZS_MAX_ALLOC_SIZE) | 1007 | if (size > ZS_MAX_ALLOC_SIZE) |
954 | size = ZS_MAX_ALLOC_SIZE; | 1008 | size = ZS_MAX_ALLOC_SIZE; |
1009 | pages_per_zspage = get_pages_per_zspage(size); | ||
1010 | |||
1011 | /* | ||
1012 | * size_class is used for normal zsmalloc operation such | ||
1013 | * as alloc/free for that size. Although it is natural that we | ||
1014 | * have one size_class for each size, there is a chance that we | ||
1015 | * can get more memory utilization if we use one size_class for | ||
1016 | * many different sizes whose size_class have same | ||
1017 | * characteristics. So, we makes size_class point to | ||
1018 | * previous size_class if possible. | ||
1019 | */ | ||
1020 | if (prev_class) { | ||
1021 | if (can_merge(prev_class, size, pages_per_zspage)) { | ||
1022 | pool->size_class[i] = prev_class; | ||
1023 | continue; | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | class = kzalloc(sizeof(struct size_class), GFP_KERNEL); | ||
1028 | if (!class) | ||
1029 | goto err; | ||
955 | 1030 | ||
956 | class = &pool->size_class[i]; | ||
957 | class->size = size; | 1031 | class->size = size; |
958 | class->index = i; | 1032 | class->index = i; |
1033 | class->pages_per_zspage = pages_per_zspage; | ||
959 | spin_lock_init(&class->lock); | 1034 | spin_lock_init(&class->lock); |
960 | class->pages_per_zspage = get_pages_per_zspage(size); | 1035 | pool->size_class[i] = class; |
961 | 1036 | ||
1037 | prev_class = class; | ||
962 | } | 1038 | } |
963 | 1039 | ||
964 | pool->flags = flags; | 1040 | pool->flags = flags; |
965 | 1041 | ||
966 | return pool; | 1042 | return pool; |
1043 | |||
1044 | err: | ||
1045 | zs_destroy_pool(pool); | ||
1046 | return NULL; | ||
967 | } | 1047 | } |
968 | EXPORT_SYMBOL_GPL(zs_create_pool); | 1048 | EXPORT_SYMBOL_GPL(zs_create_pool); |
969 | 1049 | ||
@@ -971,9 +1051,15 @@ void zs_destroy_pool(struct zs_pool *pool) | |||
971 | { | 1051 | { |
972 | int i; | 1052 | int i; |
973 | 1053 | ||
974 | for (i = 0; i < ZS_SIZE_CLASSES; i++) { | 1054 | for (i = 0; i < zs_size_classes; i++) { |
975 | int fg; | 1055 | int fg; |
976 | struct size_class *class = &pool->size_class[i]; | 1056 | struct size_class *class = pool->size_class[i]; |
1057 | |||
1058 | if (!class) | ||
1059 | continue; | ||
1060 | |||
1061 | if (class->index != i) | ||
1062 | continue; | ||
977 | 1063 | ||
978 | for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { | 1064 | for (fg = 0; fg < _ZS_NR_FULLNESS_GROUPS; fg++) { |
979 | if (class->fullness_list[fg]) { | 1065 | if (class->fullness_list[fg]) { |
@@ -981,7 +1067,10 @@ void zs_destroy_pool(struct zs_pool *pool) | |||
981 | class->size, fg); | 1067 | class->size, fg); |
982 | } | 1068 | } |
983 | } | 1069 | } |
1070 | kfree(class); | ||
984 | } | 1071 | } |
1072 | |||
1073 | kfree(pool->size_class); | ||
985 | kfree(pool); | 1074 | kfree(pool); |
986 | } | 1075 | } |
987 | EXPORT_SYMBOL_GPL(zs_destroy_pool); | 1076 | EXPORT_SYMBOL_GPL(zs_destroy_pool); |
@@ -999,8 +1088,8 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
999 | { | 1088 | { |
1000 | unsigned long obj; | 1089 | unsigned long obj; |
1001 | struct link_free *link; | 1090 | struct link_free *link; |
1002 | int class_idx; | ||
1003 | struct size_class *class; | 1091 | struct size_class *class; |
1092 | void *vaddr; | ||
1004 | 1093 | ||
1005 | struct page *first_page, *m_page; | 1094 | struct page *first_page, *m_page; |
1006 | unsigned long m_objidx, m_offset; | 1095 | unsigned long m_objidx, m_offset; |
@@ -1008,9 +1097,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
1008 | if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) | 1097 | if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) |
1009 | return 0; | 1098 | return 0; |
1010 | 1099 | ||
1011 | class_idx = get_size_class_index(size); | 1100 | class = pool->size_class[get_size_class_index(size)]; |
1012 | class = &pool->size_class[class_idx]; | ||
1013 | BUG_ON(class_idx != class->index); | ||
1014 | 1101 | ||
1015 | spin_lock(&class->lock); | 1102 | spin_lock(&class->lock); |
1016 | first_page = find_get_zspage(class); | 1103 | first_page = find_get_zspage(class); |
@@ -1031,11 +1118,11 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
1031 | obj_handle_to_location(obj, &m_page, &m_objidx); | 1118 | obj_handle_to_location(obj, &m_page, &m_objidx); |
1032 | m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); | 1119 | m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); |
1033 | 1120 | ||
1034 | link = (struct link_free *)kmap_atomic(m_page) + | 1121 | vaddr = kmap_atomic(m_page); |
1035 | m_offset / sizeof(*link); | 1122 | link = (struct link_free *)vaddr + m_offset / sizeof(*link); |
1036 | first_page->freelist = link->next; | 1123 | first_page->freelist = link->next; |
1037 | memset(link, POISON_INUSE, sizeof(*link)); | 1124 | memset(link, POISON_INUSE, sizeof(*link)); |
1038 | kunmap_atomic(link); | 1125 | kunmap_atomic(vaddr); |
1039 | 1126 | ||
1040 | first_page->inuse++; | 1127 | first_page->inuse++; |
1041 | /* Now move the zspage to another fullness group, if required */ | 1128 | /* Now move the zspage to another fullness group, if required */ |
@@ -1051,6 +1138,7 @@ void zs_free(struct zs_pool *pool, unsigned long obj) | |||
1051 | struct link_free *link; | 1138 | struct link_free *link; |
1052 | struct page *first_page, *f_page; | 1139 | struct page *first_page, *f_page; |
1053 | unsigned long f_objidx, f_offset; | 1140 | unsigned long f_objidx, f_offset; |
1141 | void *vaddr; | ||
1054 | 1142 | ||
1055 | int class_idx; | 1143 | int class_idx; |
1056 | struct size_class *class; | 1144 | struct size_class *class; |
@@ -1063,16 +1151,16 @@ void zs_free(struct zs_pool *pool, unsigned long obj) | |||
1063 | first_page = get_first_page(f_page); | 1151 | first_page = get_first_page(f_page); |
1064 | 1152 | ||
1065 | get_zspage_mapping(first_page, &class_idx, &fullness); | 1153 | get_zspage_mapping(first_page, &class_idx, &fullness); |
1066 | class = &pool->size_class[class_idx]; | 1154 | class = pool->size_class[class_idx]; |
1067 | f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); | 1155 | f_offset = obj_idx_to_offset(f_page, f_objidx, class->size); |
1068 | 1156 | ||
1069 | spin_lock(&class->lock); | 1157 | spin_lock(&class->lock); |
1070 | 1158 | ||
1071 | /* Insert this object in containing zspage's freelist */ | 1159 | /* Insert this object in containing zspage's freelist */ |
1072 | link = (struct link_free *)((unsigned char *)kmap_atomic(f_page) | 1160 | vaddr = kmap_atomic(f_page); |
1073 | + f_offset); | 1161 | link = (struct link_free *)(vaddr + f_offset); |
1074 | link->next = first_page->freelist; | 1162 | link->next = first_page->freelist; |
1075 | kunmap_atomic(link); | 1163 | kunmap_atomic(vaddr); |
1076 | first_page->freelist = (void *)obj; | 1164 | first_page->freelist = (void *)obj; |
1077 | 1165 | ||
1078 | first_page->inuse--; | 1166 | first_page->inuse--; |
@@ -1124,7 +1212,7 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, | |||
1124 | 1212 | ||
1125 | obj_handle_to_location(handle, &page, &obj_idx); | 1213 | obj_handle_to_location(handle, &page, &obj_idx); |
1126 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); | 1214 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); |
1127 | class = &pool->size_class[class_idx]; | 1215 | class = pool->size_class[class_idx]; |
1128 | off = obj_idx_to_offset(page, obj_idx, class->size); | 1216 | off = obj_idx_to_offset(page, obj_idx, class->size); |
1129 | 1217 | ||
1130 | area = &get_cpu_var(zs_map_area); | 1218 | area = &get_cpu_var(zs_map_area); |
@@ -1158,7 +1246,7 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle) | |||
1158 | 1246 | ||
1159 | obj_handle_to_location(handle, &page, &obj_idx); | 1247 | obj_handle_to_location(handle, &page, &obj_idx); |
1160 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); | 1248 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); |
1161 | class = &pool->size_class[class_idx]; | 1249 | class = pool->size_class[class_idx]; |
1162 | off = obj_idx_to_offset(page, obj_idx, class->size); | 1250 | off = obj_idx_to_offset(page, obj_idx, class->size); |
1163 | 1251 | ||
1164 | area = this_cpu_ptr(&zs_map_area); | 1252 | area = this_cpu_ptr(&zs_map_area); |