2014년 5월 27일 화요일

[Linux Kernel] 55주차(2014.05.24) 후기

ARM10C 55주차 후기

일시 : 2014.05.24 (55주차)

모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C

장소 : 토즈 타워점

장소지원 : NAVER 개발자 커뮤니티 지원 프로그램

참여인원 : 5명

스터디 진도 :

  • 지난 스터디에 이어서 mem_init()을 계속 분석합니다.
  • start_kernel()-> mm_init()->kmem_cache_init()->create_boot_cache() 분석중
  • kmem_cache_init()이 분석 중이어서 이번 후기는 내용이 짧습니다.
    • 내용이 복잡해서 slub()이 끝나는 시점에서 내용 정리를 함께 다시 하기로 했습니다.
    • 자세한 후기도 그때 다시 정리하려고 합니다.

스터디 주요 내용

  • __mod_zone_page_state()가 여러번 반복 호출 되는데, 이 중요한 함수를 복습하자.

buffered_rmqueue()

  • __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
  • __count_zone_vm_events(PGALLOC, zone, 1 << order);
    //
  • zone_statistics(preferred_zone, zone, gfp_flags);
    // NULL function
  • local_irq_restore(flags);
    // cpsr 복원
  • VM_BUG_ON(bad_range(zone, page));
    // return 0; VM_BUG_ON : 부정되어 패스
  • if (prep_new_page(page, order, gfp_flags))
    • prep_new_page(page, order, gfp_flags)
      // page migratetype이 MIGRATE_UNMOVABLE인 page, order:0, gfp_flags: 0x221200
static int prep_new_page(struct page *page, int order, gfp_t gfp_flags)
{
    int i;

    for (i = 0; i < (1 << order); i++) {
        struct page *p = page + i;
        if (unlikely(check_new_page(p)))
            return 1;
    }

    set_page_private(page, 0);
    set_page_refcounted(page);

    arch_alloc_page(page, order);
    kernel_map_pages(page, 1 << order, 1);

    if (gfp_flags & __GFP_ZERO)
        prep_zero_page(page, order, gfp_flags);

    if (order && (gfp_flags & __GFP_COMP))
        prep_compound_page(page, order);

    return 0;
}

if (unlikely(check_new_page(p)))

static inline int check_new_page(struct page *page)
{
    if (unlikely(page_mapcount(page) |
        (page->mapping != NULL)  |
        (atomic_read(&page->_count) != 0)  |
        (page->flags & PAGE_FLAGS_CHECK_AT_PREP) |
        (mem_cgroup_bad_page_check(page)))) {
        bad_page(page);
        // page->mapping != NULL :
        // atomic_read(&page->_count) != 0
        // page->flags & PAGE_FLAGS_CHECK_AT_PREP :
        // mem_cgroup_bad_page_check : FALSE
        return 1;
    }
    return 0;
}
  • // page->mapping != NULL :
    • _mapcount : to show when pag is mapped & limit reverse map searches
      • -127 초기화는
      • -1 사용하려고 할당받았지만, 참조는 되지 않음 (-1) : 0xffff ffff, 1111 …. 1111
    • mapping
  • // atomic_read(&page->_count) != 0
    • __free_pages_bootmem()->set_page_count(p,0);
    • free_pages->put_pagetestzero(page)
      // page->count: 0
      이값은 buddy초기화(
      free_pages()에서 0으로 초기화를 했다.
  • // page->flags & PAGE_FLAGS_CHECK_AT_PREP :
  • // mem_cgroup_bad_page_check : FALSE
    • check_new_page()
  • 이 의미가 bad pages가 되지 조건이다.
    // page->_mapcount : - 1 사용하기 위해 할당받았지만 참조되어 사용하지는 않음
    // page->mapping 할당 받았으면 NULL로 디, 사용중인 page는 vam, inode등의 address apace로 매핑된다.
    // page->_count :

set_page_private(page, 0);

  • page : migragtetype이 MIGRATE_UNMOVEABLE인 page
  • page->private: 0
  • 왜 private : order값을 0으로 했을까? FIX me!

set_page_refcounted(page);

  • page->_count를 1로 set

arch_alloc_page(page, order);

  • null function

kernel_map_pages(page, 1 << order, 1);

  • null function

if (gfp_flags & __GFP_ZERO) prep_zero_page(page, order, gfp_flags);

  • gfp_flags : 0x221200
  • __GFP_ZERO : 0x 8000
  • gfp_flags & __GFP_ZERO : 0

if (order && (gfp_flags & __GFP_COMP)) prep_compound_page(page, order);

  • order : 0

return 0;

  • 이것으로 get_page_from_freelist() 함수의
    this_zone_full: 의 조건인 함수의
    if(page) break;를 통해서 loop를 빠져 나옴.get_page_from_freelist()
  • alloc_pages_nodemask()->
    page = get_page_from_freelist(gfp_mask|
    GFP_HARDWALL, nodemask, order,
          zonelist, high_zoneidx, alloc_flags,
          preferred_zone, migratetype);
    

__alloc_pages_nodemask()

  • if (unlikely(!page)) // false
  • out : if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page)) goto retry_cpuset;
    // cpuset_mems_cookie : 0
  • put_mems_allowed(0) // true
  • memcg_kmem_commit_charge(page, memcg, order);
    // * page : migragtetype이 MIGRATE_UNMOVEABLE인 page , memcg : null , order : 0
    // memcg_kme_commit_charge () // null function
  • return * page
    // migragtetype이 MIGRATE_UNMOVEABLE인 page로 할당 받음

    alloc_pages_exact_node()

    // return * page : migragtetype이 MIGRATE_UNMOVEABLE인 page

alloc_slab_page()

// return * page : migragtetype이 MIGRATE_UNMOVEABLE인 page

allocate_slab()

page = alloc_slab_page(alloc_gfp, node, oo);
// return * page : migragtetype이 MIGRATE_UNMOVEABLE인 page
if (unlikely(!page))
// faluse
if (kmemcheck_enabled && page && !(s->flags & (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS)))
// kmemcheck_enabled: 0, page: migratetype이 MIGRAT_UNMOVEABLE으로 할당 받은 page
// s->flags : SLAB_HWCACHE_ALIGN : 0x00002000
// SLAB_NOTRACK : 0x000000000UL
// #define DEBUG_DEFAULT_FLAGS (SLAB_DEBUG_FREE | SLAB_RED_ZONE | \
SLAB_POISON | SLAB_STORE_USER)
// SLAB_DEBUG_FREE 0x00000100UL
// SLAB_RED_ZONE 0x00000400UL
// SLAB_POISON 0x00000800UL
// SLAB_STORE_USER 0x00010000UL
// 0x00010d00 : 0x00000100UL | 0x00000400UL | 0x00000800UL | 0x00010000UL
if (flags & __GFP_WAIT)
local_irq_disable();
// (flags : 0 & GRP_WAIT : 0x10 ) : false
if (!page)
return NULL;
page->objects = oo_objects(oo);
// oo: boot_mem_cache_node.oo
// oo_objects(oo) : 64
// page->objects : 64
// page-> union struct { //slub :
inuse : 16;
object : 15;
frozen : 1;
// page->_mapcount : 0x0040 00000
  • object의 의미는 무엇일까?
    4k안에 64짜리 object가 64개 있다.
mod_zone_page_state(page_zone(page),
(s->flags & SLAB_RECLAIM_ACCOUNT) ?
NR_SLAB_RECLAIMABLE : NR_SLAB_UNRECLAIMABLE,
1 << oo_order(oo));
// page_zone(page) : &(&contig_page_data)->node_zones[ZONE_NORMAL]
// s->flags : (&boot_kmem_cache_node)->flags: SLAB_HWCACHE_ALIGN: 0x00002000
// NR_SLAB_RECLIMABLE : 13, NR_SLAB_UNRECLAIMABLE : 14
// oo_order(boot_kmem_cache_node.oo) : 0
// 따라서 &(&contig_page_data)->node_zones[ZONE_NORMAL],NR_SLAB_UNRECLAIMABLE : 14, 0
// __mod_zone_page_state()를 호출
// zone->vm_stat[14] : (&contig_page_data)->node_zones[ZONE_NORMAL].vm_stat[14]:1
// NR_SLAB_UNRECLAIMABLE : 14, vm_stat[14]:1

new_slab()

order = compound_order(page);
// page->flags
// compound가 접착이라는 의미이므로 page가 붙여서 할당 받았는지 여부를 나타내는 것으로 추정된다.
// 우리는 작은 것을 받았으므로 0일것이다. 하지만 의미는 분명하게 확인해 보자.
// free_pages_check에서 버디를 활성화하기 전에 모두 0으로 초기화 하고 왔다.
// page->flags의 NR_PAGEFLAGS 만큼의 하위 비트를 전부 지워줌.
static inline int compound_order(struct page *page)
{
    // PageHead(page): 0
    if (!PageHead(page))
        return 0;
    return (unsigned long)page[1].lru.prev;
}

git log

Updating d9a9639..f3708db
Fast-forward
include/linux/cpuset.h | 1 +
include/linux/gfp.h | 12 +
include/linux/kmemcheck.h | 1 +
include/linux/memcontrol.h | 2 
include/linux/mm.h | 3 +

include/linux/mmzone.h | 2 
include/linux/page-flags.h | 1 +
include/linux/slab.h | 8 +++
-
include/linux/vmstat.h | 15 ++-
include/trace/events/kmem.h | 1 +
mm/bootmem.c | 2 
mm/internal.h | 3 +

mm/page_alloc.c | 77 ++———
mm/slub.c | 40 +++
mm/vmstat.c | 64 +++—————-
15 files changed, 205 insertions(+), 27 deletions(-)

2014년 5월 19일 월요일

5 가지 소프트웨어 정의 기술 도와주는 데이터 센터 자원 관리



5 가지 소프트웨어 정의 기술은 데이터 센터 관리를 쉽게 

새로운 소프트웨어 정의 도구들은 데이터 센터 환경에서의 원격지와 관련된 어려움과 복잡성을 줄일 수 있도록 도와줍니다.
“소프트웨어 정의”에 대해서 이야기 들었습니까? 소프트웨어 정의 기술은 점점 중요해 지고 있다. 특히 데이터 센터에 소프트웨어 정의 기술이 이용되면서 기존 데이터 센터에서 어려움이 있던 원격지에 대한 문제 해결에 도와 주고 있다. 여기서 새로운 소프트웨어 정의 도구가 데이터 센터에 어떻게 어려움을 해결해 주는 방법을 살펴 보자.

1. WAN 최적화.

WAN 최적화는 정말 먼 길을 왔음에도 우리는 초종 사용자 컴퓨팅 환경을 고려하여 데이터 센터 간의 통신을 개선하고 향상 시키기 위해서 노력해 왔다. 이제 소프트웨어 정의 기술을 이용하여 국내 원격지 뿐말 아니라 다수의 국가에 걸친 데이터 센터, 혹은 고객사의 통신을 최적화 할수 있다.

2. 글로벌 서버로드 밸런싱.

데이터 센터를보다 원활하게 운용하기 위한 방법으로, 엔지니어는 지속적으로 단일 장애 지점을 제거 할 수 있는 방법을 찾는다. 그런 의미에서, GSLB 주변 기술은 국가의 데이터 센터 재해 복구 사이트를 더 민첩하게 해결 할 수있다. 이제 사용자는 비즈니스 연속성 요구 사항에 맞춰 계속 전체 사이트 사이의 부하 균형을 조정할 수 있게 되었다.

3. 데이터 센터 운영 시스템.

데이터 센터 인프라 관리 (DCIM)의 다음 진화 단계로 생각할 수 있다. 데이터 센터 관리자는 멀티 사이트 수준의 인프라를 자동화를 통해서 구축 할 수 있다. 하나의 사이트가 더 많은 자원을 필요로하거나 일부 사용자를 오프로드 할 때, 다른 자원을 모니터링 하여 적합한 자원 배분을 선택할 수 있게 된것이다. 이는 데이터 센터 관리 기술이 소프트웨어 정의 기술의 발달에 맞추어 점점 더 관리와 운용이 쉬워진다는 것을 의미한다.

4. 스토리지 시스템을 분산.

분산 저장, 파일 및 복구 시스템은 모든 데이터 센터가 서로 연결 되어 있다. 스토리지의 목표는 쉽게 관리할 수 있어야 하며 제어가능한 논리 데이터 센터를 만드는 데 있다.

5. 오픈 소스 컴퓨팅.

오픈 소스 기술은 깜짝 놀랄만큼 폭발적으로 발전하고 있다. 가상화 뿐만 아니라 네트워킹, 스토리지 및 관리 산업까지 영역을 확장하고 있다. OpenStack은 같은 기술은 직접 클라우드 인프라 통합을 가능하게 해준다. 소프트웨어 정의 기술이 시장에서 보다 큰 영향을 주기 위해서는 오픈 소스 기술의 표준화가 반드시 이루어져야 한다.

결론

소프트웨어 정의 기술을 이용한 최적화 기술의 발전이 계속되는 것은 의심의 여지가 없습니다. 소프트웨어 정의 기술의 핵심 기술의 하나는 소프트웨어 정의 네트워크입니다. 이건은 기존의 물리 스위치가 하기 어려웠던 가용성과 이동성을 제공해 주지만, 여전히 물리 스위치의 많은 것을 대체하기 위해서 많은 장애와 문제를 해결해야만 한다. 하지만 소프트웨어 정의 기술은 많은 벤터의 관심과 지원으로 점점 발전하고 있기 때문에 이러한 기술이 어떻게 발전해 가는지 계속 자료를 구하고 연구하며 스스로 내제화 해야만 할 것이다.

2014년 5월 18일 일요일

[번역] 컴퓨터 네트워크 가상화 기술

OpenStack의 클라우드 환경에서 어려운 부분이 네트웍 가상화에 대한 것일 것이다. 이와 관련된 좋은 자료가 있어서 번역하였다. 
원문 : http://opencontrail.org/comparing-network-virtualization-techniques-available-in-the-cloud/

개요

클라우드 네트워크를 가상화하는 기술은이 빠르게 진화하고 있습니다. 많은 시간을 들여서 서로 다른 기술을 정리하여 구체적인 접근 방법의 장점과 단점을 이해하는 것은 자명하다.
여기서는
  1. 기존 가상화 환경에서 사용되는 네트워크 가상화 기술을 설명,
  2. 기존 및 클라우드 데이터 센터의 차이점에 대해 설명.
  3. 클라우드 데이터 센터 에서 사용되는 가상화 기술 비교.

본문

그림 1 : 레거시 데이터 센터 네트워크

post_745_image1

그림 1은 서버 가상화를 수행하는 기존 데이터 센터를 보여줍니다. 일반적으로 네트워크는 여러 L2 도메인으로 분할된다. 각 L2 도메인은 ToR(Top of Rack)에 연결된 한 쌍의 Aggregation 스위치의 세트로 구성됩니다. 가상화 서버는 ToR 스위치에 연결합니다. 여기서 "End of Row"를 변형한 설정도 있습니다만, 기본적으로 L2 도메인을 잘 정의한 범위로 나누어 연결합니다. 이러한 설계는 각 노드를 기본적으로 중첩하는 것으로 심각한 부과 나무 구조(Tree Topology)를 만듭니다. 네트워크 성능은 "용량 관리" 과정을 거쳐 각 집합 스위치에 추가 링크/포트 등을 추가하여 관리합니다.대부분의 경우, 네트워크, 그들은 동일한 아키텍처 기반하고 있음에도 불구하고, 데이터 센터의 다른 부분에서 다르게 보입니다.
그림 2 : 기존의 데이터 센터 네트워크 가상화

post_745_image2

그림 2의 네트워크 가상화는 이러한 기존 네트워크를 구현한 방법을 보여줍니다. 일반적으로 (전체 또는 선택한 VLAN을 전송) 이더넷 트렁크의 ToR 에서 연장된 각 가상화 서버로 전환됩니다. 서버에서 인스턴스화된 가상 머신은, 1 이상의 VLAN에 연결합니다. 그림과 같이 VLAN을 확장하며, 이것은 L2 도메인의 크기에 의해 제한됩니다.
대부분의 경우 단일 라우팅 공간은 L2/L3 스위치에서 지원됩니다. 모든 VLAN의 트래픽은 집계 스위치로 라우팅됩니다. 패킷 필터 ACL (액세스 제어 목록)도 일반적으로 각 VLAN 인터페이스의 포트 집합 스위치에 적용됩니다.
그림 3 : 레거시 데이터 센터 의 멀티 테넌트

post_745_image3

중복된 주소 공간의 여러 테넌트를 지원하는 시나리오를 고려하면 VRF-Lite(Virtual Routing Forwading without MPLS) 나 MPLS같은 VRF를 사용해서 aggetation 계층에 각 테넌트를 위한 독립된 라우팅 공간을 설정합니다.
대부분의 경우, 이 방법은 각 테넌트에 전용 물리적 방화벽과 로드 밸런서 장비를 별도의 쌍이 필요합니다. 그림 3 에서와 같이 핵심 계층은 여러 L2 도메인에 걸쳐 확장하여 연결되기 위하여 MPLS ( LDP 등과 같은 제어 플레인 프로토콜과 함께)으로 활성화 할 필요가 있습니다.
그림 4 : 클라우드 데이터 센터에서 기본적인 네트워크 가상화

post_745_image4
클라우드 데이터 센터의 많은 ToR 스위치에 L3( 라우팅 )을 사용하여 구축합니다. OSPF 또는 BGP와 같은 L3 라우팅 프로토콜을 쉽게 고밀도 맞물려 토폴로지 (CLOS)를 지원하고 여러 비용이 드는 흐름을 분산하여 대칭 IP 패브릭을 이용할 수 있기 때문에 이것은 주로 사용합니다. 여러 데이터 센터 사이에 걸쳐있을 수 있도록 IP 네트워크나 보편성을 설정합니다.
이러한 데이터 센터는 L2 도메인이 부족할수 있기 때문에 VLAN구조물는 적합하지 않습니다. 따라서 IP 네트워크에서 그것을 전달하는 IP 패킷으로 각 이더넷 프레임을 캡슐화 한 VXLAN ( 또는 NVGRE ) 등 의 캡슐화 메커니즘을 일부 사용합니다. 그림 4에서 각 색 점선은 비슷한 L2 네트워크를 나타냅니다. 각 가상 서버에서 실행된 소프트 스위치 ( 가상 스위치 )만 일반 L2입니다. L2 LAN을 제외하고 모든 트래픽은 가상 머신 (VM ) 으로 실행되는 소프트웨어 라우터로 전송됩니다. 경우 따라서 가상 스위치는 LAN간의 트래픽을 전환 능력을 부여하고 있지만, 게이트웨이는 일반적으로 가상 머신으로 실행되는 소프트웨어 라우터입니다.
이러한 설정에서 각 테넌트는 잠재적으로 게이트웨이 역할을 하는 소프트웨어 라우터 쌍을 가져옵니다. 그들은 용량과 필요에 따라 추가 라우터를 입수하거나, 패킷 필터링, NAT ( 네트워크 주소 변환 ) 등과 같은 정책 구현하여 라우팅합니다.
post_745_image5
그림 5 : 고급 서버 기반 기능을 갖춘 멀티 테넌트 클라우드 데이터 센터
그러나 동작하는 복수의 소프트웨어 라우터의 복잡성을 해결 하는 데 도움이 되는 기술이 있습니다. 그림 5는 가상화 된 서버의 각 소프트웨어 스위치가 (비) 캡슐 스위칭 , 라우팅, 인라인 패킷을 실행 시킬 수 있는 방법을 보여줍니다. 따라서 각 가상 서버에 있는 커널 모듈을 직접 NAT를 실행하고 다중 VRF 라우터, 패킷 필터링 및 외부 액세스 합니다. 이러한 전개는 소프트웨어 라우터의 여러 인스턴스를 자연스럽게 사용하는 것이며, 운영 대상은 아닙니다.

post_745_image6
그림 6 : 멀티 테넌트 클라우드 데이터 센터의 가상 서비스
그림 5에 설명된 기술을 활용한 deployment는 클라우드 데이터센터 안의 가상머신을 멀티 테넌트로 쉽게 추상화 할 수 있다. 각각의 테넌트는 네트워크간에 보안정책 안에서 여러 개의 가상 네트워크를 가질 수 있다. 이 테넌트는 또한 각 인스턴스 기반의 security group을 가지거나 가상 네트워크간에 필요하면 가상화된 서비스 인스턴스 (예, virtual firewall, virtual DDos 방어기 등등)를 추가할 수 있다.
일반적으로 이러한 데이터센터는 외부 퍼블릭 네트워크에 게이트웨이로 동작할 IP en(de)capsulation 및 MBGP(multi-protocol BGP)를 지원하는 표준 라우터가 필요하다. 따라서 이 접근법에서는 라우터 안의 ASIC의 극한의 전송 능력이 활용된다.
이 접근법의 또 다른 장점은 게이트웨이 라우터의 동일한 세트를 통해 서비스 제공자 L3VPN 인프라와 쉽게 통합된다는 것이다.
post_745_image7
그림 7 : 멀티 테넌트 다중 데이터 센터 의 클라우드 네트워크
그림 7은 네트워크 가상화 기술이 어떻게 여러 데이터센터 너머로 쉽게 멀티 테넌트 클라우드 인프라를 구성하는지 보여준다. 일반적으로 다른 데이터센터를 서로 연결하는 하부의 구성요소(underlying fabric)은 IP다. IP encapsulation을 사용하는 클라우드 가상화 기술은 쉽게 여러 데이터센터간에 확장할 수 있다.
모든 데이터 센터는 외부 네트워크 또는 서비스 공급자 L3VPN 인프라에 액세스하기 위해 일반적인 라우터 게이트웨이를 사용할 수 있습니다.

결론

중요한 클라우드 서비스 제공 업체와 대형 엔터프라이즈 네트워크는 구식의 L2-L3 분리에 기반에서 네트워크 scaling에 인위적인 제약조건을 두지 않는 클라우드 네트워크 가상화 기술에 관심을 기울이고 있다. 그들은 멀티 데이터센터, scale-out 방식으로 대용량으로 운영되는 멀티 테넌트 L2-L3 지원하는 기술과 솔류션을 찾고있다. 그래서 이 기술은, 오픈소스화 된것뿐만 아니라 프로토콜 통신(interaction)에서도 공개 표준에 기반을 두어 명확하게 설명할 필요가 있습니다. 
흥미롭게도, OpenContrail처럼 이기술들(그림 5,6, 7)은 이런 요구사항 중에서 많은 부분을 만족합니다. 벤더 / 공급자 / 기업은 그들의 세계적인 성공으로 이어질 전략적인 기술을 채용하고 있습니다.

2014년 5월 17일 토요일

[Linux Kernel] 54주차(2014.05.17) 후기

일시 : 2014.05.17 (54주차)

모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C

장소 : 토즈 타워점

장소지원 : NAVER 개발자 커뮤니티 지원 프로그램

참여인원 : 4명

스터디 진도 :

  • 지난 스터디에 이어서 mem_init()을 계속 분석합니다.
  • start_kernel()-> mm_init()->kmem_cache_init()->create_boot_cache() 분석중
  • kmem_cache_init()이 분석 중이어서 이번 후기는 내용이 짧습니다.
  • 자세한 후기도 그때 다시 정리하려고 합니다.

스터디 주요 내용

  • buddy할당자에 이어서 kmem 할당자 (Slub)을 분석중입니다.
  • mem_init()->kmem_cache_init()->create_boot_cache()->kmem_cache_create()
    ->kmem_cache_open()->init_kmem_cache_nodes->early_kmem_cache_node_alloc()
    ->new_slab()->allocate_slab()->alloc_slab_page()->alloc_pages_exact_node()
    ->
    alloc_pages()->__alloc_pages_nodemask();

vm_stat[?]

  • vm_stat[0] : number of free pags 수
  • vm_stat[1] : batch : chunk size for buddy add/remove

create_boot_cache()

  • mem_init()->kmem_cache_init()->create_boot_cache()
    err = __kmem_cache_create(s, flags);
    
  • __kmem_cache_create()
    // s: &boot_kmem_cache_node, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
    err = kmem_cache_open(s, flags);
    
  • kmem_cache_open()
    // s: &boot_kmem_cache_node
    if (!init_kmem_cache_nodes(s))
  • init_kmem_cache_nodes
        // slab_state: DOWN: 1
       if (slab_state == DOWN) {
           // node: 0
           early_kmem_cache_node_alloc(node);
           continue;
       }
    
  • early_kmem_cache_node_alloc()
    // kmem_cache_node: &boot_kmem_cache_node, GFP_NOWAIT: 0, node: 0
    
    page = new_slab(kmem_cache_node, GFP_NOWAIT, node);
  • new_slab()
    // s: &boot_kmem_cache_node, flags: GFP_NOWAIT: 0, node: 0
    // GFP_RECLAIM_MASK: 0x13ef0, GFP_CONSTRAINT_MASK: 0x60000
    page = allocate_slab(s,
       flags & (GFP_RECLAIM_MASK | GFP_CONSTRAINT_MASK), node);
    
  • allocate_slab()
    // alloc_gfp: 0x1200, node: 0, oo: boot_kmem_cache_node.oo
    page = alloc_slab_page(alloc_gfp, node, oo);
  • alloc_slab_page()
    // node: 0, NUMA_NO_NODE: -1
    
    if (node == NUMA_NO_NODE)
       return alloc_pages(flags, order);
    
    else
       // node: 0, flags: 0x201200, order: 0
       return alloc_pages_exact_node(node, flags, order);
    
  • alloc_pages_exact_node()
    // gfp_mask: 0x201200, order: 0, nid: 0,
    // node_zonelist(0, 0x201200): contig_page_data->node_zonelists
    return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));
  • __alloc_pages()
    // gfp_mask: 0x201200, order: 0
    // zonelist: contig_page_data->node_zonelists, NULL
    return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);
  • __alloc_pages_nodemask();
    // gfp_mask: 0x201200, GFP_HARDWALL: 0x20000, nodemask: NULL, order: 0
    // zonelist: contig_page_data->node_zonelists, high_zoneidx: ZONE_NORMAL: 0
    // alloc_flags: 0x41, preferred_zone: (&contig_page_data)->node_zones[0]
    // migratetype: MIGRATE_UNMOVABLE: 0
    page = get_page_from_freelist(gfp_mask|
    GFP_HARDWALL, nodemask, order,
           zonelist, high_zoneidx, alloc_flags,
           preferred_zone, migratetype);
    
  • get_page_from_freelist()
       // zone: contig_page_data->node_zones[0], order: 0, mark: 0
       // classzone_idx: 0 alloc_flags: 0x41
       // zone_watermark_ok(contig_page_data->node_zones[0], 0, 0, 0, 0x41): 1
       if (!zone_watermark_ok(zone, order, mark,
                      classzone_idx, alloc_flags)) {
    
  • zone_watermark_ok()
    // z: contig_page_data->node_zones[0], order: 0, mark: 0
    // classzone_idx: 0 alloc_flags: 0x41
    // zone_page_state(contig_page_data->node_zones[0], NR_FREE_PAGES): ????
    return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags,
                   zone_page_state(z, NR_FREE_PAGES));
    
    // return 1
  • get_page_from_freelist()
       // preferred_zone: (&contig_page_data)->node_zones[0]
       // zone: contig_page_data->node_zones[0], order: 0, gfp_mask: 0x221200
       // migratetype: MIGRATE_UNMOVABLE: 0
       page = buffered_rmqueue(preferred_zone, zone, order,
                       gfp_mask, migratetype);
    
  • buffered_rmqueue()
        if (list_empty(list)) {
           pcp->count += rmqueue_bulk(zone, 0,
                   pcp->batch, list,
                   migratetype, cold);
           if (unlikely(list_empty(list)))
               goto failed;
       }
    
  • rmqueue_bulk()
    for (i = 0; i < count; ++i) {
       struct page *page = __rmqueue(zone, order, migratetype);
       if (unlikely(page == NULL))
           break;
    
  • __rmqueue()
    retry_reserve:
    page = __rmqueue_smallest(zone, order, migratetype);
  • __rmqueue_smallest()
    // order : 0, MAX_ORDER : 11
    for (current_order = order; current_order < MAX_ORDER; ++current_order) {
      // zone->free_area[0] : contig_page_data_node_zones[0].free_area[0]>
       area = &(zone->free_area[current_order]);
      // area: &(contig_page_data_node_zones[0].free_area[0])
    
      // migratetype : 0,
      // area->free_list[0] : &&(contig_page_data_node_zones[0].free_area[0])->free_list[0] :1
       if (list_empty(&area->free_list[migratetype]))
           continue;
    
    • if (list_empty(&area->free_list[migratetype]))
      • 여기서 free_list[0]이 의미하는 바를 알아보자.
      • free_list가 현재 오더에서 빈것이 있는지 찾는다.
      • 빈것이 없으면 continue로 오더가 증가한다.
      • 빈것이 있으면 다음주로 실행된다.
      • 그런데 migratetype : 0은 UNMOVEABLE이므로,
      • if가 NULL이되어
      • free_list[]는 __free_one_page에서 넣었다.
    • if (unlikely(!page) && migratetype != MIGRATE_RESERVE) {
      page : NULL, migragetype : 0, MIGRATE_RESERVE : 3
      // if문 실행
      page = __rmqueue_fallback(zone, order, migratetype);
  • __rmqueue_fallback()
      // MAX_ORDER : 11, order : 0
       for (current_order = MAX_ORDER-1; current_order >= order;
                          --current_order) {
          for (i = 0;; i++) {
          // i:0, start_migratetype:0, fallback[0][0]: MIGRATE_RECLAIMABLE: 1
          // i:1, start_migratetype:0, fallback[0][1]: MIGRATE_MOVABLE : 2
              migratetype = fallbacks[start_migratetype][i];
              // fallbacks[0][0] : MIGRATE_RECLAIMABLE : 1
              // migratetype : MIGRATE_RECLAIMABLE : 1
              // fallbacks[0][1] : MIGRATE_MOVABLE : 2
              // migratetype : MIGRATE_MOVABLE : 2
    
              /* MIGRATE_RESERVE handled later if necessary */
              if (migratetype == MIGRATE_RESERVE)
                  break;
    
              // current_order : 10
              area = &(zone->free_area[current_order]);
              // area : &(zone->free_area[10]:
              //      : contig_page_data->node_zones[0].free_area[10]
    
              // i:0, migratetype : MIGRATE_RECLIMABLE : 1
              // i:0, area->free_list[1] : contig_page_data->node_zones[0].free_area[10]->free_list[1]
              // i:1, migratetype : MIGRATE_MOVABLE : 2
              // i:1, area->free_list[2] : contig_page_data->node_zones[0].free_area[10]->free_list[2]
              if (list_empty(&area->free_list[migratetype]))
                  continue;
    
              //  current_order:5, migratetype : MIGRATE_MOVABLE: 2 아래 코드 수행할것으로 보고 분석
              // i:1, curren_order:5
              // i:1 ,are->free_list[2]: contig_page_data->node_zones[0].free_area[5]->free_list[2].next
              page = list_entry(area->free_list[migratetype].next,
                      struct page, lru);
              // i:1 area->nr_free : (&contig_page_data->node_zones[0].free_area[5]->nr_free 1감소
              area->nr_free--;
    
              // i:1 zone : contig_page_data->node_zone[0]
              // i
              new_type = try_to_steal_freepages(zone, page,
                                start_migratetype,
                                migratetype);
    
              /* Remove the page from the freelists */
              list_del(&page->lru);
              rmv_page_order(page);
    
              expand(zone, page, order, current_order, area,
                     new_type);
    
              trace_mm_page_alloc_extfrag(page, order, current_order,
                  start_migratetype, migratetype, new_type);
    
              return page;
          }
      }
    
    • new_type
             new_type = try_to_steal_freepages(zone, page,
                               start_migratetype,
                               migratetype);
      
  • try_to_steal_freepages()
    static int try_to_steal_freepages(struct zone *zone, struct page *page,
                    int start_type, int fallback_type)
    {
      int current_order = page_order(page);
    
      /*
       * When borrowing from MIGRATE_CMA, we need to release the excess
       * buddy pages to CMA itself.
       */
      if (is_migrate_cma(fallback_type))
          return fallback_type;
    
      /* Take ownership for orders >= pageblock_order */
      // current_order : 5 >= pageblock_order : 10
      if (current_order >= pageblock_order) {
          change_pageblock_range(page, current_order, start_type);
          return start_type;
      }
    
      // current_order : 5
      // page_group_by_mobility_disabled : 0
      if (current_order >= pageblock_order / 2 ||
          start_type == MIGRATE_RECLAIMABLE ||
          page_group_by_mobility_disabled) {
          int pages;
    
          pages = move_freepages_block(zone, page, start_type);
    
          /* Claim the whole block if over half of it is free */
          if (pages >= (1 << (pageblock_order-1)) ||
                  page_group_by_mobility_disabled) {
    
              set_pageblock_migratetype(page, start_type);
              return start_type;
          }
    
      }
    
      return fallback_type;
    }
    
    • pages
          pages = move_freepages_block(zone, page, start_type);
      
    • move_freepages_block()
      zone : contig_page_data->node_zones[0],
  • move_freepages_block()
    int move_freepages_block(struct zone *zone, struct page *page,
                  int migratetype)
    {
      unsigned long start_pfn, end_pfn;
      struct page *start_page, *end_page;
    
      // 가정 : order 5의 migratetype MIGRARE_MOVALE 인 lru page
      //        의 pfn값을 0x200000 으로 가정하고 분석
    
      start_pfn = page_to_pfn(page);
      // start_pfn : 0x20000
    
      // pageblock_nr_pages : 0x400 (1024)
      start_pfn = start_pfn & ~(pageblock_nr_pages-1);
      // start_pfn = 0x20000 
    
      start_page = pfn_to_page(start_pfn);
      // start_page : page 0x20000 (pfn)
    
      end_page = start_page + pageblock_nr_pages - 1;
      // end_page : page 0x20CFF (pfn)
      end_pfn = start_pfn + pageblock_nr_pages - 1;
      // end_pfn : 0x20000 + ( 0x400 -1)
      // end_pfn : 0x20CFF
    
      /* Do not cross zone boundaries */
      // zone : contig_page_data->node_zones[0], start_pfn: 0x20000
      // zone_spans_pfn : 1
      if (!zone_spans_pfn(zone, start_pfn))
          start_page = page;
    
      // zone : contig_page_data->node_zones[0], end_pfn: 0x20cff
      // zone_spans_pfn : 1
      if (!zone_spans_pfn(zone, end_pfn))
          return 0;
    
      return move_freepages(zone, start_page, end_page, migratetype);
    }
    
    • return
      return move_freepages(zone, start_page, end_page, migratetype);
      
  • move_freepages()

// Move the free pages in a range to the free lists of the requested type.
// Note that start_page and end_pages are not aligned on a pageblock
// boundary. If alignment is required, use move_freepages_block()

int move_freepages(struct zone *zone,
              struct page *start_page, struct page *end_page,
              int migratetype)
{
    struct page *page;
    unsigned long order;
    int pages_moved = 0;

#ifndef CONFIG_HOLES_IN_ZONE
    /*
     * page_zone is not safe to call in this context when
     * CONFIG_HOLES_IN_ZONE is set. This bug check is probably redundant
     * anyway as we check zone boundaries in move_freepages_block().
     * Remove at a later date when no bug reports exist related to
     * grouping pages by mobility
     */
     BUG_ON(page_zone(start_page) != page_zone(end_page));
     // 시작과 끝이 같은 존에 있는지 확인하여 아니면 버그.
#endif

    // start: page : 0x20000 (pfn), end_page : page 0x20CFF (pfn)
    for (page = start_page; page <= end_page;) {
        /* Make sure we are not inadvertently changing nodes */

        // page_to_nid(page)
        VM_BUG_ON(page_to_nid(page) != zone_to_nid(zone));
        // 같은 존에 있는지 확인.

        if (!pfn_valid_within(page_to_pfn(page))) {
            page++;
            continue;
        }

        if (!PageBuddy(page)) {
            page++;
            continue;
        }

        order = page_order(page);
        // order : 5

        // zone->free_area[5[.free_list[0]: cotig_page_data->node_zones[0].free_area[5].free_area[0]
        list_move(&page->lru,
              &zone->free_area[order].free_list[migratetype]);

        // 에 page : 0x20000 (pfn)
        // page->lru를 자기 자신으로 초기화하고, free_list에 불인다.
        // migratetype 을 2에 있는 page를 0으로 바꾸고, 
        // zone->free_area[5[.free_list[0]: cotig_page_data->node_zones[0].free_area[5].free_area[2]
        // contig_page_data->node_zones[0].free_list[2] 에서
        // contig_page_data->node_zone[0.free_area[5].free_list[0]로 옮김
        set_freepage_migratetype(page, migratetype);
        // migrage type 이 2에서 0으로 바뀜.

        page += 1 << order;
        pages_moved += 1 << order;
    }

    return pages_moved;
    // pages_moved : 1024 (옴겨진 page 수)
}
  • VM_BUG_ON() : page의 nid와 zone의 nid가 일치하는지 ?
        VM_BUG_ON(page_to_nid(page) != zone_to_nid(zone));
    
  • page_to_nid()
    static inline int page_to_nid(const struct page *page)
    {
      return (page->flags >> NODES_PGSHIFT) & NODES_MASK;
      // NODES_PGSHIFT : 0,
      // NODES_MASK : 0,
      // return (page->flags >> 0) & 3; 하위 2비트만 리턴.
    }
    
    • zone_to_nid()
      return 0;
    • pfn_valid_within(page_to_pfn(page))
      if (!pfn_valid_within(page_to_pfn(page))) {
      page++;
      continue;
    • pfn_valid_within()
      return 1;
    • PageBuddy(page)
          if (!PageBuddy(page)) {
             page++;
             continue;
         }
      
    • PageBuddy
      return 1;
  • try_to_steal_freepages()
       if (pages >= (1 << (pageblock_order-1)) ||
               page_group_by_mobility_disabled) {
    
          // pages: ???
          // >
            set_pageblock_migratetype(page, start_type);
           return start_type;
    
    • set_pageblock_migratetype()
      page 블록의 migratetype을 바꿔줌 (2->0)
  • try_to_steal_freepages()
    return start_type : 0
  • __rmqueue_fallback()
    new_type = try_to_steal_freepages() : 0
    o list_del(&page->lru);
    rmv_page_order(page);
  • rmv_page_order()
    page->_mapcount : -1
    page->private : 0
    // page의 _mapcount 를 -1, private값을 0으로 초기화
  • __rmqueue_fallback()
    expand(zone, page, order, current_order, area,
                  new_type);
    
    // i: 1, order ; 0, current_order : 5, start_migratetype:
    // new_type : 0
    return page;
    // page : migratetype이 MIGRATE_UNMOVEABLE인 page

expand ():

  • 페이지가 0이 필요한데. MIGRATE_UNMOVABLE이 없어서.
  • MIGRATE_MOVEABLE을 order 5부터 4, 3, 2, 1으로 해서
  • order 1의 slub을 MIGRAT_UNMOVEABLE로 설정하고
  • 나머지 5부터 반쪽씩 free로 설정한다.
  • smallest에서 MIGRATE_UNMOVEABLE이 있었으면 프리페이지 확장을 하지만,
  • 우리는 smallest가 없었기 때문에 unlikely를 들어와서 fallback에서
  • 가장 큰 영역부터 (오더 5)에서 부터 순차적으로 내려오면서.
  • 남은 영역을 free로 확장해서 설정하고,
  • 오더 1에서 MIGRATE_MOVEABLE에서 할당 받아 MiGRATE_UNMOVEABLE로 설정하고
  • 역시 남은 영역을 free로 확장해서 할당한다.
  • (&contig_page_data->node_zones[0].free_area[4])->free_list[0] 에
  • order 5의 migratetype MIGRATE_MOVEABLE인 lru page + 16 인 page추가
  • free page 수 증가
  • page의 _mapcount 값을 -128, private를 4로 변경.
static inline void expand(struct zone *zone, struct page *page,
    int low, int high, struct free_area *area,
    int migratetype)
{
    // high : current_order : 5
    unsigned long size = 1 << high;

    // high : 5, low : 0
    while (high > low) {
         // area: &contig_page_data->node_zones[0].free_area[5]
        area--;
         // area: &contig_page_data->node_zones[0].free_area[4]

        // high : 5
        high--;
        // high : 4

        // size : 32
        size >>= 1;
        // size : 16

        // zone : contig_page_data->node_zones[0]
        // page : order 5의 migratetype MIGRATE_MOVEABLE인 lru page
        VM_BUG_ON(bad_range(zone, &page[size]));

#ifdef CONFIG_DEBUG_PAGEALLOC  // not set
        if (high < debug_guardpage_minorder()) {
            /*
             * Mark as guard pages (or page), that will allow to
             * merge back to allocator when buddy will be freed.
             * Corresponding page table entries will not be touched,
             * pages will stay not present in virtual address space
             */
            INIT_LIST_HEAD(&page[size].lru);
            set_page_guard_flag(&page[size]);
            set_page_private(&page[size], high);
            /* Guard pages are not available for any usage */
            __mod_zone_freepage_state(zone, -(1 << high),
                          migratetype);
            continue;
        }
#endif
        // size : 16
        // page : order 5의 migratetype MIGRATE_MOVEABLE인 lru page + 16).lru
        // migratetype : 0 ,
        // area->free_list[0] : &contig_page_data->node_zones[0].free_area[4])->free_list[0]
        list_add(&page[size].lru, &area->free_list[migratetype]);
        // &contig_page_data->node_zones[0].free_area[4])->free_list[0]에
        // order K의 migratetype MIGRATE_MOVEABLE lru page + 16 인 page 추가

        // (&contig_page_data->node_zones[0].free_area[4]->free_list[0]
        area->nr_free++;
        // free page 수 증가

        // size : 16,
        // page[16] : order 5의 migratetype 이 MIGRATE_MOVEABLE인 lru page
        set_page_order(&page[size], high);
        // page의 _mapcount 값을 -128, private를 4로 변경.

    }
}
  • expand(zone, page, order, current_order, area, new_type)
    • order는 필요한 크기
    • current_order : 버디에서 가져온 크기
    • 큰 버디 오더에서 사용하려는 오더를 제외한 나머지 버디영역을
    • 미사용영역으로 정리함.
    • (필요한 크기보다 가져온 크기가 클때 )실행함.

__rmqueue()

  • 해당 zone에서 order(크기)에 맞고 migratetype이 같은 page를 구해옴
  • migratetype이 일치 하지 않으면.
  • fallbacks의 enum의 메모리정책에 따라서 할당 받는다.
    1. __rmque_samllest () UNMOVERABLE
    2. __rmqure_fallback () MOVEABLE
    3. RESERVE에서 찾는다.
         if (!page) {
          migratetype = MIGRATE_RESERVE;
          goto retry_reserve;
      
trace_mm_page_alloc_zone_locked(page, order, migratetype);
// page : migratetype MIGRATE_
  • fallback에서 메모리 정책이 결정된다.

requeue_bulk()

    struct page *page = __rmqueue(zone, order, migratetype);
위의 분석이었다.
  // cold :0>
   if (likely(cold == 0))
       list_add(&page->lru, list);
        // page->lru : (migtratetype이 MIGRATE_UNMOVEABLE인 page)->lru
        // list : (&boot_pageset + (__per_cpu_offset[0]))->pcp.lists[0]

   else
       list_add_tail(&page->lru, list);
   // page의 lru가 cold가 1일때 tail로 cold가 0일때 head로 등록됨

  // IS_ENABLED(CONFIG_CMA)  // n/A
   if (IS_ENABLED(CONFIG_CMA)) {
       mt = get_pageblock_migratetype(page);
       if (!is_migrate_cma(mt) && !is_migrate_isolate(mt))
           mt = migratetype;
   }

  // page->index = migratetype
   set_freepage_migratetype(page, mt);
  // page의 index필드를 migratetype 0으로 변경

  // list : (&boot_pageset + (__per_cpu_offset[0]))->pcp.lists[0]
  // page->lru : (migratetype 이 MIGRATE_UNMOVEABLE인 page)->lru
  list = &page->lru;
  // lru는 색인이고, 자료는 pcp.list[0]에 값이 업데이트 됨

  // mt : 0
  if (is_migrate_cma(mt)) // false
       __mod_zone_page_state(zone, NR_FREE_CMA_PAGES,
                     -(1 << order));
 }
 // zone: contig_page_data->node_zones[0], NR_FREE_PAGES : 0, i:1
 __mod_zone_page_state(zone, NR_FREE_PAGES, -(i << order));
// vm_stat[enum]에서 NR_FREE_PAGES인 2번째 인자를 가지고 update하는 함수
 // x = delta + __this_cpu_read(*p)
// (&contig_pgate_data)->node_zones[ZONE_NORMAL].vm_stat[0]의 값을 update함
// t : 32???6 -1 5
 return i;
 // order : 0, return i : 1

buffered_rmqueue()

   if (list_empty(list)) {
       pcp->count += rmqueue_bulk(zone, 0,
               pcp->batch, list,
               migratetype, cold);
        // pcp->count : (&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0]
       if (unlikely(list_empty(list))) // fault
           goto failed;
   }
  • // cold : 0
     if (cold)
         page = list_entry(list->prev, struct page, lru);
     else
        // list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0])->next
         page = list_entry(list->next, struct page, lru);
        // page : migratetype이 MIGRATE_UNMOVEABEL인 page
      list_del(&page->lru);
    // list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0]에서 현제 page->lru삭제
     pcp->count--;
    //  list-next :((&boot_pageset + (__per_cpu_offset[0]))->pcp.list[0]).count : 0
    
    // gfp_flags : 0x221200
    if (!gfp_thisnode_allocation(gfp_flags)) // true : !false 
        // zone: contig_page_data->noce_zones[0], NR_ALLOC_BATCH :1, order: 0
         __mod_zone_page_state(zone, NR_ALLOC_BATCH, -(1 << order));
        // (&contig_page_data->node_zones[0]->vm_stat[1] update
        // 
    

스터디 log

4824920..d9a9639 master -> origin/master
Merge made by the ‘recursive’ strategy.
arch/arm/include/asm/spinlock.h | 2 +
include/asm-generic/memory_model.h | 1 +
include/linux/list.h | 3 +
include/linux/lockdep.h | 2 +
include/linux/mm.h | 16 -
include/linux/mmzone.h | 6 +
include/linux/page-flags-layout.h | 1 +
include/linux/pageblock-flags.h | 4 +-
include/linux/preempt.h | 1 +
include/linux/spinlock.h | 5 +
include/linux/spinlock_api_smp.h | 3 +
include/linux/vmstat.h | 1 +
include/trace/events/kmem.h | 2 +
kernel/locking/spinlock.c | 2 +
kernel/locking/spinlock_debug.c | 5 +
mm/internal.h | 1 +
mm/page_alloc.c | 281 ++
-
mm/vmstat.c | 10 +-
18 files changed, 335 insertions(+), 11 deletions(-)