diff options
-rw-r--r-- | mm/zsmalloc.c | 126 |
1 files changed, 98 insertions, 28 deletions
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index 0dec1fa5f656..6f3cfbf5e237 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c | |||
@@ -110,6 +110,8 @@ | |||
110 | #define ZS_MAX_ZSPAGE_ORDER 2 | 110 | #define ZS_MAX_ZSPAGE_ORDER 2 |
111 | #define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) | 111 | #define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER) |
112 | 112 | ||
113 | #define ZS_HANDLE_SIZE (sizeof(unsigned long)) | ||
114 | |||
113 | /* | 115 | /* |
114 | * Object location (<PFN>, <obj_idx>) is encoded as | 116 | * Object location (<PFN>, <obj_idx>) is encoded as |
115 | * as single (unsigned long) handle value. | 117 | * as single (unsigned long) handle value. |
@@ -140,7 +142,8 @@ | |||
140 | /* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ | 142 | /* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */ |
141 | #define ZS_MIN_ALLOC_SIZE \ | 143 | #define ZS_MIN_ALLOC_SIZE \ |
142 | MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) | 144 | MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS)) |
143 | #define ZS_MAX_ALLOC_SIZE PAGE_SIZE | 145 | /* each chunk includes extra space to keep handle */ |
146 | #define ZS_MAX_ALLOC_SIZE (PAGE_SIZE + ZS_HANDLE_SIZE) | ||
144 | 147 | ||
145 | /* | 148 | /* |
146 | * On systems with 4K page size, this gives 255 size classes! There is a | 149 | * On systems with 4K page size, this gives 255 size classes! There is a |
@@ -233,14 +236,24 @@ struct size_class { | |||
233 | * This must be power of 2 and less than or equal to ZS_ALIGN | 236 | * This must be power of 2 and less than or equal to ZS_ALIGN |
234 | */ | 237 | */ |
235 | struct link_free { | 238 | struct link_free { |
236 | /* Handle of next free chunk (encodes <PFN, obj_idx>) */ | 239 | union { |
237 | void *next; | 240 | /* |
241 | * Position of next free chunk (encodes <PFN, obj_idx>) | ||
242 | * It's valid for non-allocated object | ||
243 | */ | ||
244 | void *next; | ||
245 | /* | ||
246 | * Handle of allocated object. | ||
247 | */ | ||
248 | unsigned long handle; | ||
249 | }; | ||
238 | }; | 250 | }; |
239 | 251 | ||
240 | struct zs_pool { | 252 | struct zs_pool { |
241 | char *name; | 253 | char *name; |
242 | 254 | ||
243 | struct size_class **size_class; | 255 | struct size_class **size_class; |
256 | struct kmem_cache *handle_cachep; | ||
244 | 257 | ||
245 | gfp_t flags; /* allocation flags used when growing pool */ | 258 | gfp_t flags; /* allocation flags used when growing pool */ |
246 | atomic_long_t pages_allocated; | 259 | atomic_long_t pages_allocated; |
@@ -269,6 +282,34 @@ struct mapping_area { | |||
269 | enum zs_mapmode vm_mm; /* mapping mode */ | 282 | enum zs_mapmode vm_mm; /* mapping mode */ |
270 | }; | 283 | }; |
271 | 284 | ||
285 | static int create_handle_cache(struct zs_pool *pool) | ||
286 | { | ||
287 | pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE, | ||
288 | 0, 0, NULL); | ||
289 | return pool->handle_cachep ? 0 : 1; | ||
290 | } | ||
291 | |||
292 | static void destroy_handle_cache(struct zs_pool *pool) | ||
293 | { | ||
294 | kmem_cache_destroy(pool->handle_cachep); | ||
295 | } | ||
296 | |||
297 | static unsigned long alloc_handle(struct zs_pool *pool) | ||
298 | { | ||
299 | return (unsigned long)kmem_cache_alloc(pool->handle_cachep, | ||
300 | pool->flags & ~__GFP_HIGHMEM); | ||
301 | } | ||
302 | |||
303 | static void free_handle(struct zs_pool *pool, unsigned long handle) | ||
304 | { | ||
305 | kmem_cache_free(pool->handle_cachep, (void *)handle); | ||
306 | } | ||
307 | |||
308 | static void record_obj(unsigned long handle, unsigned long obj) | ||
309 | { | ||
310 | *(unsigned long *)handle = obj; | ||
311 | } | ||
312 | |||
272 | /* zpool driver */ | 313 | /* zpool driver */ |
273 | 314 | ||
274 | #ifdef CONFIG_ZPOOL | 315 | #ifdef CONFIG_ZPOOL |
@@ -595,13 +636,18 @@ static void *obj_location_to_handle(struct page *page, unsigned long obj_idx) | |||
595 | * decoded obj_idx back to its original value since it was adjusted in | 636 | * decoded obj_idx back to its original value since it was adjusted in |
596 | * obj_location_to_handle(). | 637 | * obj_location_to_handle(). |
597 | */ | 638 | */ |
598 | static void obj_handle_to_location(unsigned long handle, struct page **page, | 639 | static void obj_to_location(unsigned long handle, struct page **page, |
599 | unsigned long *obj_idx) | 640 | unsigned long *obj_idx) |
600 | { | 641 | { |
601 | *page = pfn_to_page(handle >> OBJ_INDEX_BITS); | 642 | *page = pfn_to_page(handle >> OBJ_INDEX_BITS); |
602 | *obj_idx = (handle & OBJ_INDEX_MASK) - 1; | 643 | *obj_idx = (handle & OBJ_INDEX_MASK) - 1; |
603 | } | 644 | } |
604 | 645 | ||
646 | static unsigned long handle_to_obj(unsigned long handle) | ||
647 | { | ||
648 | return *(unsigned long *)handle; | ||
649 | } | ||
650 | |||
605 | static unsigned long obj_idx_to_offset(struct page *page, | 651 | static unsigned long obj_idx_to_offset(struct page *page, |
606 | unsigned long obj_idx, int class_size) | 652 | unsigned long obj_idx, int class_size) |
607 | { | 653 | { |
@@ -860,12 +906,16 @@ static void __zs_unmap_object(struct mapping_area *area, | |||
860 | { | 906 | { |
861 | int sizes[2]; | 907 | int sizes[2]; |
862 | void *addr; | 908 | void *addr; |
863 | char *buf = area->vm_buf; | 909 | char *buf; |
864 | 910 | ||
865 | /* no write fastpath */ | 911 | /* no write fastpath */ |
866 | if (area->vm_mm == ZS_MM_RO) | 912 | if (area->vm_mm == ZS_MM_RO) |
867 | goto out; | 913 | goto out; |
868 | 914 | ||
915 | buf = area->vm_buf + ZS_HANDLE_SIZE; | ||
916 | size -= ZS_HANDLE_SIZE; | ||
917 | off += ZS_HANDLE_SIZE; | ||
918 | |||
869 | sizes[0] = PAGE_SIZE - off; | 919 | sizes[0] = PAGE_SIZE - off; |
870 | sizes[1] = size - sizes[0]; | 920 | sizes[1] = size - sizes[0]; |
871 | 921 | ||
@@ -1153,13 +1203,14 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, | |||
1153 | enum zs_mapmode mm) | 1203 | enum zs_mapmode mm) |
1154 | { | 1204 | { |
1155 | struct page *page; | 1205 | struct page *page; |
1156 | unsigned long obj_idx, off; | 1206 | unsigned long obj, obj_idx, off; |
1157 | 1207 | ||
1158 | unsigned int class_idx; | 1208 | unsigned int class_idx; |
1159 | enum fullness_group fg; | 1209 | enum fullness_group fg; |
1160 | struct size_class *class; | 1210 | struct size_class *class; |
1161 | struct mapping_area *area; | 1211 | struct mapping_area *area; |
1162 | struct page *pages[2]; | 1212 | struct page *pages[2]; |
1213 | void *ret; | ||
1163 | 1214 | ||
1164 | BUG_ON(!handle); | 1215 | BUG_ON(!handle); |
1165 | 1216 | ||
@@ -1170,7 +1221,8 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, | |||
1170 | */ | 1221 | */ |
1171 | BUG_ON(in_interrupt()); | 1222 | BUG_ON(in_interrupt()); |
1172 | 1223 | ||
1173 | obj_handle_to_location(handle, &page, &obj_idx); | 1224 | obj = handle_to_obj(handle); |
1225 | obj_to_location(obj, &page, &obj_idx); | ||
1174 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); | 1226 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); |
1175 | class = pool->size_class[class_idx]; | 1227 | class = pool->size_class[class_idx]; |
1176 | off = obj_idx_to_offset(page, obj_idx, class->size); | 1228 | off = obj_idx_to_offset(page, obj_idx, class->size); |
@@ -1180,7 +1232,8 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, | |||
1180 | if (off + class->size <= PAGE_SIZE) { | 1232 | if (off + class->size <= PAGE_SIZE) { |
1181 | /* this object is contained entirely within a page */ | 1233 | /* this object is contained entirely within a page */ |
1182 | area->vm_addr = kmap_atomic(page); | 1234 | area->vm_addr = kmap_atomic(page); |
1183 | return area->vm_addr + off; | 1235 | ret = area->vm_addr + off; |
1236 | goto out; | ||
1184 | } | 1237 | } |
1185 | 1238 | ||
1186 | /* this object spans two pages */ | 1239 | /* this object spans two pages */ |
@@ -1188,14 +1241,16 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle, | |||
1188 | pages[1] = get_next_page(page); | 1241 | pages[1] = get_next_page(page); |
1189 | BUG_ON(!pages[1]); | 1242 | BUG_ON(!pages[1]); |
1190 | 1243 | ||
1191 | return __zs_map_object(area, pages, off, class->size); | 1244 | ret = __zs_map_object(area, pages, off, class->size); |
1245 | out: | ||
1246 | return ret + ZS_HANDLE_SIZE; | ||
1192 | } | 1247 | } |
1193 | EXPORT_SYMBOL_GPL(zs_map_object); | 1248 | EXPORT_SYMBOL_GPL(zs_map_object); |
1194 | 1249 | ||
1195 | void zs_unmap_object(struct zs_pool *pool, unsigned long handle) | 1250 | void zs_unmap_object(struct zs_pool *pool, unsigned long handle) |
1196 | { | 1251 | { |
1197 | struct page *page; | 1252 | struct page *page; |
1198 | unsigned long obj_idx, off; | 1253 | unsigned long obj, obj_idx, off; |
1199 | 1254 | ||
1200 | unsigned int class_idx; | 1255 | unsigned int class_idx; |
1201 | enum fullness_group fg; | 1256 | enum fullness_group fg; |
@@ -1204,7 +1259,8 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle) | |||
1204 | 1259 | ||
1205 | BUG_ON(!handle); | 1260 | BUG_ON(!handle); |
1206 | 1261 | ||
1207 | obj_handle_to_location(handle, &page, &obj_idx); | 1262 | obj = handle_to_obj(handle); |
1263 | obj_to_location(obj, &page, &obj_idx); | ||
1208 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); | 1264 | get_zspage_mapping(get_first_page(page), &class_idx, &fg); |
1209 | class = pool->size_class[class_idx]; | 1265 | class = pool->size_class[class_idx]; |
1210 | off = obj_idx_to_offset(page, obj_idx, class->size); | 1266 | off = obj_idx_to_offset(page, obj_idx, class->size); |
@@ -1236,7 +1292,7 @@ EXPORT_SYMBOL_GPL(zs_unmap_object); | |||
1236 | */ | 1292 | */ |
1237 | unsigned long zs_malloc(struct zs_pool *pool, size_t size) | 1293 | unsigned long zs_malloc(struct zs_pool *pool, size_t size) |
1238 | { | 1294 | { |
1239 | unsigned long obj; | 1295 | unsigned long handle, obj; |
1240 | struct link_free *link; | 1296 | struct link_free *link; |
1241 | struct size_class *class; | 1297 | struct size_class *class; |
1242 | void *vaddr; | 1298 | void *vaddr; |
@@ -1244,9 +1300,15 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
1244 | struct page *first_page, *m_page; | 1300 | struct page *first_page, *m_page; |
1245 | unsigned long m_objidx, m_offset; | 1301 | unsigned long m_objidx, m_offset; |
1246 | 1302 | ||
1247 | if (unlikely(!size || size > ZS_MAX_ALLOC_SIZE)) | 1303 | if (unlikely(!size || (size + ZS_HANDLE_SIZE) > ZS_MAX_ALLOC_SIZE)) |
1304 | return 0; | ||
1305 | |||
1306 | handle = alloc_handle(pool); | ||
1307 | if (!handle) | ||
1248 | return 0; | 1308 | return 0; |
1249 | 1309 | ||
1310 | /* extra space in chunk to keep the handle */ | ||
1311 | size += ZS_HANDLE_SIZE; | ||
1250 | class = pool->size_class[get_size_class_index(size)]; | 1312 | class = pool->size_class[get_size_class_index(size)]; |
1251 | 1313 | ||
1252 | spin_lock(&class->lock); | 1314 | spin_lock(&class->lock); |
@@ -1255,8 +1317,10 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
1255 | if (!first_page) { | 1317 | if (!first_page) { |
1256 | spin_unlock(&class->lock); | 1318 | spin_unlock(&class->lock); |
1257 | first_page = alloc_zspage(class, pool->flags); | 1319 | first_page = alloc_zspage(class, pool->flags); |
1258 | if (unlikely(!first_page)) | 1320 | if (unlikely(!first_page)) { |
1321 | free_handle(pool, handle); | ||
1259 | return 0; | 1322 | return 0; |
1323 | } | ||
1260 | 1324 | ||
1261 | set_zspage_mapping(first_page, class->index, ZS_EMPTY); | 1325 | set_zspage_mapping(first_page, class->index, ZS_EMPTY); |
1262 | atomic_long_add(class->pages_per_zspage, | 1326 | atomic_long_add(class->pages_per_zspage, |
@@ -1268,40 +1332,45 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size) | |||
1268 | } | 1332 | } |
1269 | 1333 | ||
1270 | obj = (unsigned long)first_page->freelist; | 1334 | obj = (unsigned long)first_page->freelist; |
1271 | obj_handle_to_location(obj, &m_page, &m_objidx); | 1335 | obj_to_location(obj, &m_page, &m_objidx); |
1272 | m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); | 1336 | m_offset = obj_idx_to_offset(m_page, m_objidx, class->size); |
1273 | 1337 | ||
1274 | vaddr = kmap_atomic(m_page); | 1338 | vaddr = kmap_atomic(m_page); |
1275 | link = (struct link_free *)vaddr + m_offset / sizeof(*link); | 1339 | link = (struct link_free *)vaddr + m_offset / sizeof(*link); |
1276 | first_page->freelist = link->next; | 1340 | first_page->freelist = link->next; |
1277 | memset(link, POISON_INUSE, sizeof(*link)); | 1341 | |
1342 | /* record handle in the header of allocated chunk */ | ||
1343 | link->handle = handle; | ||
1278 | kunmap_atomic(vaddr); | 1344 | kunmap_atomic(vaddr); |
1279 | 1345 | ||
1280 | first_page->inuse++; | 1346 | first_page->inuse++; |
1281 | zs_stat_inc(class, OBJ_USED, 1); | 1347 | zs_stat_inc(class, OBJ_USED, 1); |
1282 | /* Now move the zspage to another fullness group, if required */ | 1348 | /* Now move the zspage to another fullness group, if required */ |
1283 | fix_fullness_group(pool, first_page); | 1349 | fix_fullness_group(pool, first_page); |
1350 | record_obj(handle, obj); | ||
1284 | spin_unlock(&class->lock); | 1351 | spin_unlock(&class->lock); |
1285 | 1352 | ||
1286 | return obj; | 1353 | return handle; |
1287 | } | 1354 | } |
1288 | EXPORT_SYMBOL_GPL(zs_malloc); | 1355 | EXPORT_SYMBOL_GPL(zs_malloc); |
1289 | 1356 | ||
1290 | void zs_free(struct zs_pool *pool, unsigned long obj) | 1357 | void zs_free(struct zs_pool *pool, unsigned long handle) |
1291 | { | 1358 | { |
1292 | struct link_free *link; | 1359 | struct link_free *link; |
1293 | struct page *first_page, *f_page; | 1360 | struct page *first_page, *f_page; |
1294 | unsigned long f_objidx, f_offset; | 1361 | unsigned long obj, f_objidx, f_offset; |
1295 | void *vaddr; | 1362 | void *vaddr; |
1296 | 1363 | ||
1297 | int class_idx; | 1364 | int class_idx; |
1298 | struct size_class *class; | 1365 | struct size_class *class; |
1299 | enum fullness_group fullness; | 1366 | enum fullness_group fullness; |
1300 | 1367 | ||
1301 | if (unlikely(!obj)) | 1368 | if (unlikely(!handle)) |
1302 | return; | 1369 | return; |
1303 | 1370 | ||
1304 | obj_handle_to_location(obj, &f_page, &f_objidx); | 1371 | obj = handle_to_obj(handle); |
1372 | free_handle(pool, handle); | ||
1373 | obj_to_location(obj, &f_page, &f_objidx); | ||
1305 | first_page = get_first_page(f_page); | 1374 | first_page = get_first_page(f_page); |
1306 | 1375 | ||
1307 | get_zspage_mapping(first_page, &class_idx, &fullness); | 1376 | get_zspage_mapping(first_page, &class_idx, &fullness); |
@@ -1355,20 +1424,20 @@ struct zs_pool *zs_create_pool(char *name, gfp_t flags) | |||
1355 | if (!pool) | 1424 | if (!pool) |
1356 | return NULL; | 1425 | return NULL; |
1357 | 1426 | ||
1358 | pool->name = kstrdup(name, GFP_KERNEL); | ||
1359 | if (!pool->name) { | ||
1360 | kfree(pool); | ||
1361 | return NULL; | ||
1362 | } | ||
1363 | |||
1364 | pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *), | 1427 | pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *), |
1365 | GFP_KERNEL); | 1428 | GFP_KERNEL); |
1366 | if (!pool->size_class) { | 1429 | if (!pool->size_class) { |
1367 | kfree(pool->name); | ||
1368 | kfree(pool); | 1430 | kfree(pool); |
1369 | return NULL; | 1431 | return NULL; |
1370 | } | 1432 | } |
1371 | 1433 | ||
1434 | pool->name = kstrdup(name, GFP_KERNEL); | ||
1435 | if (!pool->name) | ||
1436 | goto err; | ||
1437 | |||
1438 | if (create_handle_cache(pool)) | ||
1439 | goto err; | ||
1440 | |||
1372 | /* | 1441 | /* |
1373 | * Iterate reversly, because, size of size_class that we want to use | 1442 | * Iterate reversly, because, size of size_class that we want to use |
1374 | * for merging should be larger or equal to current size. | 1443 | * for merging should be larger or equal to current size. |
@@ -1450,6 +1519,7 @@ void zs_destroy_pool(struct zs_pool *pool) | |||
1450 | kfree(class); | 1519 | kfree(class); |
1451 | } | 1520 | } |
1452 | 1521 | ||
1522 | destroy_handle_cache(pool); | ||
1453 | kfree(pool->size_class); | 1523 | kfree(pool->size_class); |
1454 | kfree(pool->name); | 1524 | kfree(pool->name); |
1455 | kfree(pool); | 1525 | kfree(pool); |