연구 진행 상황
✅ 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로 통일해주었음.
원인 (2) calloc과 malloc의 무분별한 사용
해결 (2) calloc은 0으로 초기화를 해주기 때문에 굳이 memset을 하지 않아도 되는데 malloc()이라는 에러로 제대로 초기화가 되지 않은 줄 알았음. 아래 이미지와 같은 코드 삭제 후 calloc()으로 통일
트러블슈팅 #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);
}