본문 바로가기
연구/연구일지

2024.09.02(월) - RDMA_KVS 트러블슈팅

by sumping 2024. 9. 2.

연구 진행 상황

✅ RDMA server code - 메모리 중복 해제 해결

✅ RDMA code - put 완료

✅ RDMA code - KVS 완료

 

 

❎ 문제 여러 상황

- 클라이언트는 왜 response를 2번 출력하는가  -> 서버가 2번 보내서

- 서버는 왜 답장을 안보내는가  -> 사이즈 조정 잘못해서 (work completion error)

- get을 하면 malloc(): invalid size (unsorted) 문제  -> 냅다 이중 초기화 시켜서 (malloc)

 

 


트러블슈팅 #01. 서버 코드 메모리 누수 (double free detected in tcache 2)

 

문제상황 : 서버 코드에서 메모리 중복 해제가 지속적으로 발생

에러 로그

 

원인 : while문으로 event 관리를 하였는데 그 과정에서 rdma_ack_cm_event(event)를 두 번 호출 (while문에서 이미 호출하였는데 그 이후 다른 함수에서 event에 대한 ack을 처리하겠다고 한 번 더 코드를 넣음)

이미 앞에서 처리한 코드

 


해결
 : 해당 코드를 주석처리함 

주석처리

 

 

문제 해결 방법 : Valgrind로 메모리 누수 체크 (디버깅 및 프로파일링 툴)

valgrind --leak-check=full --show-leak-kinds=all ./server

문제 해결

 


트러블슈팅 #02. 서버 코드 메모리 할당 문제 (malloc(): invalid size (unsorted))

서버 에러 로그

 

문제 상황 : 서버 코드에서 첫 번째 put 후에 두 번째 get을 하면 malloc() 에러

 

 

원인 (1) 일정하지 않은 버퍼 사이즈 -> 어떤 버퍼는 struct message 다른 건 uint32_t

해결 (1) 통일해주었다. unint32_t는 4byte여서 사이즈가 더 큰 message로 통일해주었음.

 

원인 1

 

원인 (2) calloc과 malloc의 무분별한 사용

해결 (2) calloc은 0으로 초기화를 해주기 때문에 굳이 memset을 하지 않아도 되는데 malloc()이라는 에러로 제대로 초기화가 되지 않은 줄 알았음. 아래 이미지와 같은 코드 삭제 후 calloc()으로 통일

 

원인 2

 

 


트러블슈팅 #03. 서버 코드 remote operation error

 

서버 에러 로그

 

문제 상황 : 클라이언트가 put 요청을 보내고 서버가 put 요청 처리한 것에 대해서 답신을 보내야 되는데 답신을 보내지 못하고 work completion error 발생

 

원인 : 버퍼 사이즈를 잘못 조정함. recv 메모리 영역을 할당할 때 buffer_size로 했는데 해당 크기를 줄였다가 문제 발생

해결 : 해당 버퍼 사이즈를 늘림 (2 -> 3)

 

버퍼 사이즈

 

 

수신 메모리 영역

 


트러블슈팅 #04. 서버 코드 메모리 재할당과 재등록 (수신을 하지 못함)

 

 

문제 상황: 첫 번째 클라이언트 요청은 잘 송수신을 하지만 두 번째 요청에 대해서는 type만 출력하고 key, value를 출력하지 못함

 

원인 : 메모리가 이미 할당되고 메모리가 이미 등록되어 있는데 또 새로 할당하고 재등록하는 불필요한 코드 설계. 

해결 : 코드 수정 (이미 할당되었는지 확인하는 if문 추가)

 

해결 후 제대로 받고 처리할 수 있음

 


트러블슈팅 #05. 서버에서 Work completion error (클라이언트 코드 문제)

 

서버 - 에러 코드

문제 상황 : 서버에서 다 처리를 하고 클라이언트로 보내지 못함 or 클라이언트에서 받지 못함

 

원인 : 클라이언트에서 받지를 못하고 있었음. 왜냐면 클라이언트에서 수신 버퍼 준비가 부족했기 때문이다. 해당 원인은 Receive Not Ready (RNR) 에러이기 때문에 RDMA 수신 버퍼에 문제가 있거나 수신 큐에 문제가 있는 것임.

 

해결 : 트러블슈팅 #04를 참고해서 재할당/재등록을 하지 않으면서 수신 버퍼를 준비하도록 클라이언트 코드를 수정

static void pre_post_recv_buffer() {
    static int buffer_initialized = 0;

    if (!buffer_initialized) {
        recv_buffer = calloc(2, sizeof(struct message));  // 메시지 두 개를 받을 수 있도록 설정
        if (!recv_buffer) {
            perror("Failed to allocate memory for receive buffer");
            exit(EXIT_FAILURE);
        }

        ctx.recv_mr = ibv_reg_mr(ctx.pd, recv_buffer, 2 * sizeof(struct message), 
            IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_READ | IBV_ACCESS_REMOTE_WRITE);

        if (!ctx.recv_mr) {
            perror("Failed to register memory region");
            exit(EXIT_FAILURE);
        }

        buffer_initialized = 1;  // 버퍼 초기화 완료
    }

    recv_sge.addr = (uintptr_t)recv_buffer;
    recv_sge.length = sizeof(struct message);  // 한 번에 한 메시지를 처리한다고 가정

    recv_sge.lkey = ctx.recv_mr->lkey;

    memset(&recv_wr, 0, sizeof(recv_wr));
    recv_wr.wr_id = 0;
    recv_wr.sg_list = &recv_sge;
    recv_wr.num_sge = 1;

    if (ibv_post_recv(id->qp, &recv_wr, &bad_recv_wr)) {
        perror("Failed to post receive work request");
        exit(EXIT_FAILURE);
    }
    printf("Memory registered at address %p with LKey %u\n\n", recv_buffer, ctx.recv_mr->lkey);
}