本文共 4454 字,大约阅读时间需要 14 分钟。
zbud是一个专门为存储压缩page而设计的内存分配器.用于将两个压缩的page存到一个单独的page中.zbud page 被分成chunks,每一个chunk的size是64byte,zbud目前只有被zswap使用. 我们来实际看看zbud的源码. 源码路径如下:source/mm/zbud.c 每一个被zbud管理的memory用zbud_pool来表示,每个zbud_pool有一个zbud_header。 126 #ifdef CONFIG_ZPOOL 216 MODULE_ALIAS("zpool-zbud"); 217 #endif /* CONFIG_ZPOOL */ 被CONFIG_ZPOOL 包起来的code是提供给zpool使用的接口,基本都是调用zbud的函数.zbud的使用一般是先调用zbud_zpool_create来新建一个pool,然后zbud_zpool_malloc 从这个pool中分配memory,如果想得到物理地址就调用zbud_zpool_map 228 /* Converts an allocation size in bytes to size in zbud chunks */ 229 static int size_to_chunks(size_t size) 230 { 231 return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT; 232 } 由于zbud是以chunk 为单位管理memory的,因此将申请到size转成chunk index. 237 /* Initializes the zbud header of a newly allocated zbud page */ 238 static struct zbud_header *init_zbud_page(struct page *page) 239 { 240 struct zbud_header *zhdr = page_address(page); 241 zhdr->first_chunks = 0; 242 zhdr->last_chunks = 0; 243 INIT_LIST_HEAD(&zhdr->buddy); 244 INIT_LIST_HEAD(&zhdr->lru); 245 zhdr->under_reclaim = 0; 246 return zhdr; 247 } 每个zbud都有一个head,init_zbud_page用于初始化zbud header 305 struct zbud_pool *zbud_create_pool(gfp_t gfp, const struct zbud_ops *ops) 306 { 307 struct zbud_pool *pool; 308 int i; 309 310 pool = kzalloc(sizeof(struct zbud_pool), gfp); 311 if (!pool) 312 return NULL; 313 spin_lock_init(&pool->lock); 314 for_each_unbuddied_list(i, 0) 315 INIT_LIST_HEAD(&pool->unbuddied[i]); 316 INIT_LIST_HEAD(&pool->buddied); 317 INIT_LIST_HEAD(&pool->lru); 318 pool->pages_nr = 0; 319 pool->ops = ops; 320 return pool; 321 } 新建一个zbud_pool通过kzalloc 申请内容为0的memory,然后做zbud_pool初始化,注意这个时候还没有申请要管理的memory. int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp, 354 unsigned long *handle) 355 { 356 int chunks, i, freechunks; 357 struct zbud_header *zhdr = NULL; 358 enum buddy bud; 359 struct page *page; 360 361 if (!size || (gfp & __GFP_HIGHMEM)) 362 return -EINVAL; 363 if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE) 364 return -ENOSPC; 365 chunks = size_to_chunks(size); 366 spin_lock(&pool->lock); 367 368 /* First, try to find an unbuddied zbud page. */ 369 zhdr = NULL; 370 for_each_unbuddied_list(i, chunks) { 371 if (!list_empty(&pool->unbuddied[i])) { 372 zhdr = list_first_entry(&pool->unbuddied[i], 373 struct zbud_header, buddy); 374 list_del(&zhdr->buddy); 375 if (zhdr->first_chunks == 0) 376 bud = FIRST; 377 else 378 bud = LAST; 379 goto found; 380 } 381 } 382 383 /* Couldn't find unbuddied zbud page, create new one */ 384 spin_unlock(&pool->lock); 385 page = alloc_page(gfp); 386 if (!page) 387 return -ENOMEM; 388 spin_lock(&pool->lock); 389 pool->pages_nr++; 390 zhdr = init_zbud_page(page); 391 bud = FIRST; 392 393 found: 394 if (bud == FIRST) 395 zhdr->first_chunks = chunks; 396 else 397 zhdr->last_chunks = chunks; 398 399 if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0) { 400 /* Add to unbuddied list */ 401 freechunks = num_free_chunks(zhdr); 402 list_add(&zhdr->buddy, &pool->unbuddied[freechunks]); 403 } else { 404 /* Add to buddied list */ 405 list_add(&zhdr->buddy, &pool->buddied); 406 } 407 408 /* Add/move zbud page to beginning of LRU */ 409 if (!list_empty(&zhdr->lru)) 410 list_del(&zhdr->lru); 411 list_add(&zhdr->lru, &pool->lru); 412 413 *handle = encode_handle(zhdr, bud); 414 spin_unlock(&pool->lock); 415 416 return 0; 417 } zswap 会调用这个函数从zbud要memory. 361不能申请highmemory, 363申请memory的size不能超过一个page. 365行将申请的size 转成chunk index. 370行判断pool->unbuddied 是否有memory可以用,由于是第一次申请,这个条件不成立。 385行通过alloc_page 向buddy system申请一个page的memory。 389行pages_nr++ 记录用于zbud管理的page 个数,第一次等于1 390 init_zbud_page 初始化呢header. 399行的zhdr->last_chunks == 0 成立,将free的chunk加到pool->unbuddied 列表中. 409条件不成立 411行将新申请的pool加到lru 中 413行调用encode_handle 返回在page中的offset. 259 static unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud) 260 { 261 unsigned long handle; 262 263 /* 264 * For now, the encoded handle is actually just the pointer to the data 265 * but this might not always be the case. A little information hiding. 266 * Add CHUNK_SIZE to the handle if it is the first allocation to jump 267 * over the zbud header in the first chunk. 268 */ 269 handle = (unsigned long)zhdr; 270 if (bud == FIRST) 271 /* skip over zbud header */ 272 handle += ZHDR_SIZE_ALIGNED; 273 else /* bud == LAST */ 274 handle += PAGE_SIZE - (zhdr->last_chunks << CHUNK_SHIFT); 275 return handle; 276 } 关于zbud我有几点不明白FIRST和last 到底表示啥含义。为什么free chunks会是NCHUNKS - zhdr->first_chunks - zhdr->last_chunks 知道的大神指点下啊转载地址:http://btcmi.baihongyu.com/