1. 도입
오늘 밀워키 벅스가 NBA 우승을 했다. 매년 이 순간에 내가 뭘 했는지를 돌아보곤 하는데, 올해는 AS관에 앉아서 디버깅을 하고 있다.
기존에는 항상 NULL을 참조해서 panic이 발생했지만, 이번에는 kernel paging request를 다룰 수 없다는 bug이다. write path에서 dedup queue에 write entry를 push 하는 함수를 호출하였고, dedup system call에서 이 queue안의 write entry를 꺼내는 방식을 구현해보았다.
// fs/nova/dedup.c
// Get next Dedup queue entry
int nova_dedup_queue_get_next_entry(struct nova_file_write_entry *next_entry){
struct nova_dedup_queue *ptr;
if(nova_dedup_queue_head.list.next){
ptr = list_entry(nova_dedup_queue_head.list.next, struct nova_dedup_queue, list);
printk("checking~ %lld\n",ptr->entry_data->pgoff);
next_entry = ptr->entry_data;
return 1;
}
else{
return 0;
}
}
// Insert Write Entries to Dedup Queue
int nova_dedup_queue_push(struct nova_file_write_entry *new_entry){
struct nova_dedup_queue *new_data;
new_data = kmalloc(sizeof(struct nova_dedup_queue), GFP_KERNEL);
list_add_tail(&new_data->list, &nova_dedup_queue_head.list);
new_data->entry_data = new_entry;
return 0;
}
// fs/nova/file.c
nova_init_file_write_entry(sb, sih, &entry_data, epoch_id,
start_blk, allocated, blocknr, time,
file_size);
ret = nova_append_file_write_entry(sb, pi, inode,
&entry_data, &update);
/* NOVA DEDUP KHJ */
nova_dedup_queue_init();
printk("Dedup Queue init\n");
nova_dedup_queue_push(&entry_data);
printk("Pushed Entry %lld\n",entry_data.pgoff);
2. Test 1
어느 부분에서 오류가 발생하는지 확인하기 위해, dedup.c의 함수들에 주소와 실제 넘긴 write entry의 값들을 비교해보았다.
// fs/nova/dedup.c
// Insert Write Entries to Dedup Queue
int nova_dedup_queue_push(struct nova_file_write_entry *new_entry){
struct nova_dedup_queue *new_data;
new_data = kmalloc(sizeof(struct nova_dedup_queue), GFP_KERNEL);
list_add_tail(&new_data->list, &nova_dedup_queue_head.list);
new_data->entry_data = new_entry;
printk("push print %p\n",new_data->entry_data);
return 0;
}
// Get next Dedup queue entry
int nova_dedup_queue_get_next_entry(struct nova_file_write_entry *next_entry){
struct nova_dedup_queue *ptr;
if(nova_dedup_queue_head.list.next){
ptr = list_entry(nova_dedup_queue_head.list.next, struct nova_dedup_queue, list);
printk("checking~ %lld\n",ptr->entry_data->pgoff);
next_entry = ptr->entry_data;
return 1;
}
else{
return 0;
}
}
분석 결과, write path에서 entry_data의 주소를 저장하면 안 되었다. 이는 잠깐 dram에 두는 임시 주소일 뿐 실제로 pmeme에 저장되는 주소는 완전히 다르기 때문이다. 이 주소를 알기 위해서는 nova_append_file_write_entry를 따라 들어가야 됐다. 즉, 실제 copy가 어디서 이루어지는지, 그리고 그 주소가 정해지는 시점이 어디인지를 알고 그 주소를 dedup queue에 push 해줘야 됐다.
3. Test 2
static int nova_append_log_entry(struct super_block *sb,
struct nova_inode *pi, struct inode *inode,
struct nova_inode_info_header *sih,
struct nova_log_entry_info *entry_info)
{
void *entry, *alter_entry;
enum nova_entry_type type = entry_info->type;
struct nova_inode_update *update = entry_info->update;
u64 tail, alter_tail;
u64 curr_p, alter_curr_p;
size_t size;
int extended = 0;
unsigned long irq_flags = 0;
if (type == DIR_LOG)
size = entry_info->file_size;
else
size = nova_get_log_entry_size(sb, type);
tail = update->tail;
alter_tail = update->alter_tail;
curr_p = nova_get_append_head(sb, pi, sih, tail, size,
MAIN_LOG, 0, &extended);
if (curr_p == 0)
return -ENOSPC;
/* DEDUP NOVA KHJ */
if(type == FILE_WRITE){
nova_dedup_queue_push(curr_p);
}
/*****************/
nova_append_log_entry에서 'curr_p'가 새로 append할 주소인 것 같다. 이 값을 queue에 저장해뒀다가 나중에 nova_get_block(sb, curr_p)로 읽어오면 될 것 같다. 이 자료형은 u64이니까 출력해보자!
오~ 64B씩 증가하는 것을 볼 수 있다! 그렇다면 이 주소를 저장하는 것이 맞을 것 같다.
4. Test 3
// fs/nova/dedup.h
/* nova_dedup_queue
queue of entries that needs to be deduplicated
*/
struct nova_dedup_queue{
u64 write_entry_address;
struct list_head list;
};
// fs/nova/dedup.c
// Insert Write Entries to Dedup Queue
int nova_dedup_queue_push(u64 new_address){
struct nova_dedup_queue *new_data;
new_data = kmalloc(sizeof(struct nova_dedup_queue), GFP_KERNEL);
list_add_tail(&new_data->list, &nova_dedup_queue_head.list);
new_data->write_entry_address = new_address;
printk("push print %llu\n",new_data->write_entry_address);
return 0;
}
// Get next Dedup queue entry
u64 nova_dedup_queue_get_next_entry(void){
struct nova_dedup_queue *ptr;
if(nova_dedup_queue_head.list.next){
ptr = list_entry(nova_dedup_queue_head.list.next, struct nova_dedup_queue, list);
printk("checking~ %llu\n",ptr->write_entry_address);
return 1;
}
else{
return 0;
}
}
// fs/nova/log.c
static int nova_append_log_entry(...){
...
/* DEDUP NOVA KHJ */
if(type == FILE_WRITE){
printk("%llu\n",curr_p);
nova_dedup_queue_push(curr_p);
}
/*****************/
...
}
dedup queue의 맴버 변수를 u64로 바꾸서 주소를 저장했다.
결과 설명
우선 test파일은 'KHJ'를 총 4096번 반복해서 쓰는 fprintf문이었다. fprintf와 buffer의 구조에 따라서 총 3번의 write만 호출하는 것 같다. (더 깊이 깔린 지식은 정확히는 모르겠다) 아무튼 연속적인 write였기 때문에 그렇지 않을까 싶고 nova에서 flush 하는 단위가 4KB였을 수 있다. 중요한 것은 1298... 이 주소 값이 한 번은 nova_append_log_entry에서 찍히고 한 번은 test2.c를 돌렸을 때 dedup system call에서 찍힌다는 것이다. 아직 queue를 어디에 선언할지 정하지 않아서 매번 init 해주기 때문에 가장 마지막에 저장된 주소가 pop-out이 된다. 이제 queue의 pop-out에서 삭제를 구현해줘야 되고 어디서 선언할지를 정한 다음 fingerprinting으로 들어가야겠다.
오류 결론
다른 코드 영역의 공간을 참조하려고해서(?) , 암튼 초반 발상 자체가 잘못됨.
'실험실 (커널 오류)' 카테고리의 다른 글
[kernel panic] not syncing: Real mode trampoline was not allocated (0) | 2021.11.24 |
---|---|
[kernel] Opening files inside the Kernelspace (0) | 2021.08.01 |
[NOVA] write path - offset, pos 추적 (7/20) (0) | 2021.07.20 |
[kernel build error] VFS: Unable to mount root fs on unknown-block(0,0) (0) | 2021.07.19 |
[NOVA] system call argument 추가 오류(2) (0) | 2021.07.19 |