10차 ARM 리눅스 커널 스터디 52주차(2014.04.26) 후기입니다.
일시 : 2014.04.26 (52주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 5명
스터디 진도 :
- start_kernel()-> mm_init()->kmem_cache_init() 분석중
스터디관련 내용
start_kernel()->mm_init()->kmem_cache_init()
create_boot_cache()
void __init create_boot_cache(struct kmem_cache *s, const char *name, size_t size, unsigned long flags)
{
int err;
// s->name: boot_kmem_cache_node.name: NULL
s->name = name;
// s->name: boot_kmem_cache_node.name: "kmem_cache_node"
// s->size: boot_kmem_cache_node.size: 0
// s->object_size: boot_kmem_cache_node.object_size: 0
s->size = s->object_size = size;
// s->size: boot_kmem_cache_node.size: 44
// s->object_size: boot_kmem_cache_node.object_size: 44
// flags: SLAB_HWCACHE_ALIGN: 0x00002000UL, ARCH_KMALLOC_MINALIGN: 64, size: 44
// s->align: boot_kmem_cache_node.align: 0
s->align = calculate_alignment(flags, ARCH_KMALLOC_MINALIGN, size);
// s->align: boot_kmem_cache_node.align: 64
// s: &boot_kmem_cache_node, flags: SLAB_HWCACHE_ALIGN: 0x00002000UL
err = __kmem_cache_create(s, flags);
if (err)
panic("Creation of kmalloc slab %s size=%zu failed. Reason %d\n", name, size, err);
s->refcount = -1; /* Exempt from merging for now */
}
kmem_cache_open()
- if(!kmem_cache_has_cpu_partial(s))
- kmem_cache_open()
- // s: &boot_kmem_cache_node, kmem_cache_debug(&boot_kmem_cache_node): 0
- return !kmem_cache_debug(s);
- return 0
- kmem_cache_open()
- init_kmem_cache_nodes(s)
- if(slab_state == DOWN) {
- early_kmem_cache_node_alloc(node)
- page = new_slab(kmem_cache_node, GFP_NOWAIT, node);
- early_kmem_cache_node_alloc(node)
- if(slab_state == DOWN) {
define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH)
- GFP_ATOMIC :
- __GFP_HIGH : 0x20u
- GFP_NOWAIT : 0x0
- “attbibute((bitwise))”로 검색한 결과 중 하나를 따라가다 보니
다음과 같은 내용들을 알 수 있었습니다.리눅스 소스에 ~linux/Documentations/sparse.txt라는 파일이 있습니다. 문서 제목은 "Using sparse for typechecking"입니다. sparse는 "semantic parser"에서 따온 이름으로, 그 패키지 안에 들어있는 sparse라는 이름의 프로그램은 static analyzer라고 합니다.
sparse.txt 문서의 내용처럼, 커널 소스에서 “make C=1”이나 “make C=2”라고
명령을 주면 C 소스 파일에 대해 sparse 프로그램을 실행합니다.
그 때 “CHECKFLAGS=-DCHECKER -DCHECK_ENDIAN“ 옵션을 함께 주면
소스 코드에서 “attribute((bitwise))”가 활성화 된 상태로 sparse
프로그램이 이를 분석하게 됩니다. 그래서 “attribute((bitwise))”
속성이 일치하지 않는 변수 할당 등을 발견하면 다음과 같은 식의 메시지를 출력합니다.validation/foul-bitwise.c:9:9: warning: restricted degrades to integer validation/foul-bitwise.c:9:15: warning: restricted degrades to integer validation/foul-bitwise.c:19:9: error: incompatible types for operation (-) validation/foul-bitwise.c:19:9: argument has type restricted unsigned short [usertype] a
리눅스가 sparse를 작성하기 시작한 건 소스 내에서 사용자 메모리 공간에 대한 포인터와
커널 메모리 공간에 대한 포인터가 섞여 쓰이는 문제를 해결하기 위해서였다고 합니다.
컴파일러로는 탐지하기 힘든 코딩 상의 오류이죠. ~linux/include/linux/compiler.h에
그 내용이 들어가 있습니다. 그리고 이후 그걸 확장해서 엔디안 검사까지 하도록 확장된
듯 합니다.
- int alloc_flags = ALLOC_WMARK_LOW|ALLOC_CPUSET;
- ALLOC_WMARK_LOW : 1
- ALLOC_CPUSET : 0x40u
- struct mem_cgroup *memcg = NULL;
- 이것을 선언하는 것이 .c에서 선언을 하는 이유는?
memcontrol.h에서 선언을 하였지만, .c에서 정의한 것을 사용할 수는 있겠지만 *포인터로 선언되어 문제가 되지 알을것이다.
- gfp_mask &= gfp_allowed_mask;
- gfp_mask : 0x201200
- gfp_allowd_mask : 0x1ffff2f
- 0x201200
- lockdep_trace_alloc(gfp_mask);
- lockdep_trace_alloc(g) do { } while (0)
- null function
- lockdep_trace_alloc(g) do { } while (0)
- might_sleep_if(gfp_mask & __GFP_WAIT);
- gfp_mask : 0x201200
- __GFP_WAIT : 0x10u
- 0
- if (should_fail_alloc_page(gfp_mask, order)) return NULL;
- return false;
- if (unlikely(!zonelist->_zonerefs->zone)) return NULL;
- !zonelist->_zonerefs->zone
- (&contig_page_data)->node_zonelists->_zonerefs->zone
- if (!memcg_kmem_newpage_charge(gfp_mask, &memcg, order))
- gfp_mask: 0x201200, memcg : NULL, order : 0
- return true
- ! true : 0
- cpuset_mems_cookie = get_mems_allowed();
- return 0
- cpuset_mems_cookie : 0
- first_zones_zonelist(zonelist, high_zoneidx,
nodemask ? : &cpuset_current_mems_allowed,
&preferred_zone); - zonelist : (&contig_page_data)->node_zonelists
- N_MEMORY : N_HIGH_MEMORY : 3
- node_state[N_MEMORY]
- N_MEMORY : 3
- N_HIGH_MEMORY : 3
- node_state[N_HIGH_MEMORY] : node_state[3]
- next_zones_zonelist()
- while (zonelist_zone_idx(z) > highest_zoneidx ||
(z->zone && !zref_in_nodemask(z, nodes))) - return contig_page_data->node_zonelists->_zonerefs[1]
- perferred_zone: (&congit_page_data->node_zonelists->_zonerefs[1]
- page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order, zonelist, high_zoneidx, alloc_flags, preferred_zone, migratetype);
- gfp_mask: 0x201200
- __GFP_HARDWALL: 0x20000
- nodemask : NULL
- zonelist : contig_page_data->noze_zonelists->_zonerefs[1]
- preffered_zone : (&contig_page_data)->node_zonelists
- migratetype : 0
GFP_KERNEL 에 대하여
GFP_KERNEL가 GFP_USER보다 priority가 높다는 것은, GFP_KERNEL option으로 호출하면, kmalloc의 메모리가 부족하면 메모리가 확보될때가지 계속 시도하지만,
GFP_USER option로 호출하면, 메모리 부족하면 fail로 끝난다고 합니다.
GFP_USER option로 호출하면, 메모리 부족하면 fail로 끝난다고 합니다.
GFP_ATOMIC
it means roughly “make the allocation operation atomic”. This means that the kernel will try to find the memory using a pile of free memory set aside for urgent allocation. If that pile doesn’t have enough free pages, the operation will fail. This flag is useful for allocation within interrupt handlers.
GFP_KERNEL
it will try a little harder to find memory. There’s a
possibility that the call to kmalloc() will sleep while the kernel is trying to find memory (thus making it unsuitable for interrupt handlers). It’s much more rare for an allocation with GFP_KERNEL to fail than with GFP_ATOMIC.
In all cases, kmalloc() should only be used allocating small amounts of memory (a few kb). vmalloc() is better for larger amounts.
Also note that in lab 1 and lab 2, it would have been arguably better to use GFP_KERNEL instead of GFP_ATOMIC. GFP_ATOMIC should be saved for those instances in which a sleep would be totally unacceptable.
possibility that the call to kmalloc() will sleep while the kernel is trying to find memory (thus making it unsuitable for interrupt handlers). It’s much more rare for an allocation with GFP_KERNEL to fail than with GFP_ATOMIC.
In all cases, kmalloc() should only be used allocating small amounts of memory (a few kb). vmalloc() is better for larger amounts.
Also note that in lab 1 and lab 2, it would have been arguably better to use GFP_KERNEL instead of GFP_ATOMIC. GFP_ATOMIC should be saved for those instances in which a sleep would be totally unacceptable.
싸이월드 사진 백업을 받는 프로그램
그 외
- 스터디 시작 1년을 맞아서 조촐한 저녁 축하 파티를 했습니다.
- 가족과 함께 즐거운 시간을 보내고 다음 주(5월 10일)에 소스 분석을 계속합니다.
- Ubutu 14.04 LTS가 kernel 3.13.0을 사용합니다. LTS는 2019년까지 서비스를 계속할 예정이므로 가능하다다면 LTS 커널이 좋다는 의견이었습니다. 하지만 지금까지 한 공부한 것이 많아서, 각자 연휴기간동안 저희 커널 3.11에서 3.13으로 옴겨서 분석할 수 있을지 각자가 고민해 보기로 하였습니다.
스터디 로그
3278ad9..9ce507a master -> origin/master
Updating 3278ad9..9ce507a
Fast-forward
include/linux/cpuset.h | 5 +
include/linux/gfp.h | 107 +++
include/linux/kernel.h | 1 +
include/linux/lockdep.h | 1 +
include/linux/memcontrol.h | 3 +-
include/linux/mmzone.h | 15 +++
include/linux/nodemask.h | 14 +-
include/linux/numa.h | 2 +
include/linux/types.h | 1 +
include/uapi/linux/types.h | 1 +
mm/internal.h | 4
mm/memcontrol.c | 1 +
mm/mmzone.c | 8 ++
mm/page_alloc.c | 53 +++—-
mm/slab.h | 2 +
mm/slab_common.c | 1 +
mm/slub.c | 45 ++-
17 files changed, 254 insertions(+), 10 deletions(-)
Updating 3278ad9..9ce507a
Fast-forward
include/linux/cpuset.h | 5 +
include/linux/gfp.h | 107 +++
include/linux/kernel.h | 1 +
include/linux/lockdep.h | 1 +
include/linux/memcontrol.h | 3 +-
include/linux/mmzone.h | 15 +++
include/linux/nodemask.h | 14 +-
include/linux/numa.h | 2 +
include/linux/types.h | 1 +
include/uapi/linux/types.h | 1 +
mm/internal.h | 4
mm/memcontrol.c | 1 +
mm/mmzone.c | 8 ++
mm/page_alloc.c | 53 +++—-
mm/slab.h | 2 +
mm/slab_common.c | 1 +
mm/slub.c | 45 ++-
17 files changed, 254 insertions(+), 10 deletions(-)