GlusterFS/코드 분석/xlator/mgmt

From PGWiki


피어 상태 기계(friend-sm, peer-sm)

상태

상태명 설명
GD_FRIEND_STATE_DEFAULT 초기 상태
GD_FRIEND_STATE_REQ_SENT 피어 참여(probe) 요청을 송신한 상태
GD_FRIEND_STATE_REQ_RCVD 피어 참여 요청을 수신한 상태
GD_FRIEND_STATE_BEFRIENDED 피어가 클러스터에 참여 중인 상태
GD_FRIEND_STATE_REQ_ACCEPTED 피어 요청을 수신하여 허용까지 마친 상태
GD_FRIEND_STATE_REQ_SENT_RCVD 피어 요청과 응답의 송/수신이 완료된 상태
GD_FRIEND_STATE_REJECTED 피어 요청이 거부된 상태
GD_FRIEND_STATE_UNFRIEND_SENT 클러스터 참여 해제(detach)를 요청한 상태
GD_FRIEND_STATE_PROBE_RCVD GD_FRIEND_STATE_DEFAULT와 유사하게 특정 초기 상태를 나타내는 것으로 보여지며, 피어 참여 요청을 수신하거나 연결되었음을 수신했을 때 상태 전이가 일어남
GD_FRIEND_STATE_CONNECTED_RCVD 피어가 연결되었음을 수신한 상태
GD_FRIEND_STATE_CONNECTED_ACCEPTED 피어가 연결되었음을 수신하여 허용까지 마친 상태

이벤트

이벤트 설명
GD_FRIEND_EVENT_NONE 기본값을 나타내며, 별도 용도 없음
GD_FRIEND_EVENT_PROBE 미상... glusterd_ac_reverse_probe_begin()
GD_FRIEND_EVENT_INIT_FRIEND_REQ 피어 추가 요청(probe)에 대한 응답을 수신했을 때 발생되는 이벤트
GD_FRIEND_EVENT_RCVD_ACC 피어 참여 요청을 받았을 때의 액터인 glusterd_rpc_friend_add()를 통해 발생되는 이벤트
GD_FRIEND_EVENT_LOCAL_ACC 수신한 피어 참여 요청을 로컬 데이터와 비교(glusterd_compare_friend_data())하는 등의 로컬 정합성 검증을 하는 콜백인 glusterd_ac_handle_friend_add_req()를 통해 발생되는 이벤트
GD_FRIEND_EVENT_RCVD_RJT 피어 참여 요청(glusterd_friend_add())에 대한 응답 콜백인 glusterd_friend_add_cbk()를 통해 발생되는 이벤트
GD_FRIEND_EVENT_LOCAL_RJT GD_FRIEND_EVENT_LOCAL_ACC 항목 참고. 다만 이 이벤트는 볼륨/스냅샷 등과 같이 glusterd가 갖는 데이터의 정합성이 일치하지 않을 때에 발생되는 이벤트이다.
GD_FRIEND_EVENT_RCVD_FRIEND_REQ 피어 참여 요청을 받았을 때의 액터인 glusterd_handle_incoming_friend_req()를 통해 발생되는 이벤트
GD_FRIEND_EVENT_INIT_REMOVE_FRIEND 피어 참여 해제를 위한 도입 함수인 glusterd_deprobe_begin() 함수에서 발생되는 이벤트
GD_FRIEND_EVENT_RCVD_REMOVE_FRIEND 피어 참여 해제 요청에 대한 액터인 glusterd_handle_unfriend_req()를 통해 발생되는 이벤트
GD_FRIEND_EVENT_REMOVE_FRIEND glusterd_friend_remove_notify(), glusterd_friend_remove_cbk(), glusterd_ac_send_friend_remove_req(), glusterd_ac_handle_friend_remove_req()
GD_FRIEND_EVENT_CONNECTED 핸드 쉐이킹 과정에서 피어 정보 덤프 요청에 대한 콜백(RPC_CLNT_CONNECT -> glusterd_peer_dump_version_cbk)이나 그 안에서 호출되는 버전 협상에 대한 콜백(glusterd_mgmt_hndsk_version_ack_cbk)에서 호출하는 glusterd_event_connected_inject() 함수를 통해 발생되는 이벤트
GD_FRIEND_EVENT_NEW_NAME 피어 추가 과정 중에 호출되는 glusterd_probe_begin() 함수와 이 요청에 대한 서비스 콜백인 glusterd_probe_cbk()에서 피어가 이름만 변경된 것으로 판단될 때 발생되는 이벤트

전이도

GlusterFS Peer State-Machine
GlusterFS Peer State-Machine

절차

  1. glusterd_friend_sm()
    피어 상태 기계를 갱신하는 단일 함수
    1. while(!cds_list_empty(&gd_friend_sm_queue))
      상태 기계 이벤트를 기록하는 프로세스 전역 변수인 gd_friend_sm_queue에 쌓인 이벤트들을 순회
      1. cds_list_for_each_entry_safe(...)
        1. cds_list_del_init(event->list)
          리스트에서 꺼낸 이벤트를 리스트로부터 제거
        2. event_type = event->event
          이벤트 유형을 기록
          event_typeglusterd_friend_sm_event_type_t
          eventglusterd_friend_sm_event_t
        3. glusterd_peerinfo_find(event->peerid, event->peername)
          피어 정보(glusterd_peerinfo_t)를 조회
          이 호출 직전에 rcu 읽기 잠금을 한 뒤에 현재 상태를 얻은 직후에 이 잠금을 풀게 되는데 그 이유는 핸들러 호출에 있어서 현재 상태만이 필요하기 때문인 것도 있으며, 일부 핸들러에서 임계 영역에 해당하는 갱신 작업을 수행하고 이는 데드락으로 이어지기 때문이다.
        4. old_state = peerinfo->state.state
          이전 피어 상태를 기록
          첫번째 stateglusterd_peer_state_info_t
          두번째 stateglusterd_friend_sm_state_t
        5. state = glusterd_friend_state_table[old_state]
          stateglusterd_sm_t
        6. handler = state[event_type].handler
          handlerglusterd_friend_sm_ac_fn
        7. ret = handler(event, event.ctx)
        8. ret != 0이면 glusterd_destroy_friend_event_context(event) 호출
        9. event_typeGD_FRIEND_EVENT_REMOVE_FRIEND 혹은 GD_FRIEND_EVENT_INIT_REMOVE_FRIENDglusterd_destroy_friend_event_context(event) 호출
        10. glusterd_friend_sm_transition_state(event->peerid, event->peername, state, event_type)
        11. glusterd_peerinfo_find(event->peerid, event->peername)
          핸들러 내에서 변경이 있을 수도 있으므로 다시 갱신
        12. glusterd_gd_does_peer_affect_quorum(old_state, event_type, peerinfo)
        13. glusterd_store_peerinfo(...)
        14. glusterd_destroy_friend_event_context(event)

명령 상태 기계(op-sm)

상태

상태명 설명
GD_OP_STATE_DEFAULT 초기 상태
GD_OP_STATE_LOCK_SENT 트랜잭션을 위한 잠금 처리를 요청했음
GD_OP_STATE_LOCKED 트랜잭션을 위해 잠금 처리 되었음
GD_OP_STATE_STAGE_OP_SENT 스테이지를 요청했음
GD_OP_STATE_STAGED 스테이지 되었음
GD_OP_STATE_COMMIT_OP_SENT 커밋을 요청했음
GD_OP_STATE_COMMITED 커밋이 되었음
GD_OP_STATE_UNLOCK_SENT 트랜잭션의 잠금 해제를 요청했음
GD_OP_STATE_STAGE_OP_FAILED 스테이지 요청이 실패했음
GD_OP_STATE_COMMIT_OP_FAILED 커밋이 실패했음
GD_OP_STATE_BRICK_OP_SENT 브릭 관리 명령을 요청했음
GD_OP_STATE_BRICK_OP_FAILED 브릭 관리 명령이 실패했음
GD_OP_STATE_BRICK_COMMITTED 브릭 관리 명령이 커밋 되었음
GD_OP_STATE_BRICK_COMMIT_FAILED 브릭 관리 명령의 커밋이 실패했음
GD_OP_STATE_ACK_DRAIN 요청이 실패하여 다른 노드들로부터 ACK/NACK 수신을 대기하며, 모든 수신을 마치면 실패한 것으로 처리

이벤트

이벤트 설명
GD_OP_EVENT_NONE 기본값을 나타내며, 별도 용도 없음
GD_OP_EVENT_START_LOCK 트랜잭션을 시작하는 함수인 glusterd_op_txn_begin()호출 시, 로컬 잠금이 완료된 후에 클러스터 잠금 요청이 시작되었음을 알리는 이벤트이며, 해당 이벤트를 수신한 대상들은 모두 GD_OP_STATE_LOCK_SENT상태를 유지한다. 또한 이 이벤트는 연산 버전이 GD_OP_VERSION_3_6_0미만일 경우에만 사용
GD_OP_EVENT_LOCK 클러스터 수준의 잠금을 처리할 때 발생되는 이벤트이며, glusterd_handle_cluster_lock(), glusterd_op_state_machine_mgmt_v3_lock()을 통해 발생
GD_OP_EVENT_RCVD_ACC 클러스터/피어/브릭 잠금 및 잠금 해제 요청, 스테이지 요청, 커밋 요청, 브릭 명령 요청에 대해서 정상적으로 수행했거나, 그러한 응답을 받았을 때 발생되는 이벤트
GD_OP_EVENT_ALL_ACC GD_OP_EVENT_START_LOCK과 동일하게 트랜잭션 시작 시, 클러스터 잠금/잠금 해제 요청을 수신했음을 알리는 이벤트이며, 연산 버전이 GD_OP_VERSION_3_6_0이상일 경우에만 사용된다.
GD_OP_EVENT_STAGE_ACC 스테이지 단계 진입 후에 발생하는 이벤트
GD_OP_EVENT_COMMIT_ACC 명령 커밋 요청까지 성공했을 때 발생하는 이벤트
GD_OP_EVENT_RCVD_RJT 클러스터/피어/브릭 잠금 및 잠금 해제 요청, 스테이지 요청, 커밋 요청, 브릭 명령 요청에 대해서 실패하거나 그러한 응답을 받았을 때 발생되는 이벤트
GD_OP_EVENT_STAGE_OP 잠금 다음 단계로서, 요청에 대한 컨텍스트를 생성하는 스테이지 요청(glusterd_handle_stage_op()) 시에 발생하는 이벤트
GD_OP_EVENT_COMMIT_OP 스테이지 다음 단계로서, 수행된 명령의 커밋 요청을 보낼 때 발생하는 이벤트
GD_OP_EVENT_UNLOCK 클러스터 잠금 해제를 요청할 때(glusterd_handle_cluster_unlock()) 발생되는 이벤트
GD_OP_EVENT_START_UNLOCK 현재 사용되지 않는 것으로 보여지는 이벤트
GD_OP_EVENT_ALL_ACK 모든 지연된 노드들의(지연된 브릭도 포함) 스테이지, 커밋, 브릭 명령이 실패했을 때 발생하는 이벤트
GD_OP_EVENT_LOCAL_UNLOCK_NO_RESP 현재 사용되지 않는 것으로 보여지는 이벤트

전이도

GlusterFS Operation State-Machine
GlusterFS Operation State-Machine

절차

  1. glusterd_op_sm()
    명령 상태 기계를 갱신하는 단일 함수
    1. synclock_trylock(&gd_op_sm_lock)
    2. while(!cds_list_empty(&gd_op_sm_queue)
      1. cds_list_for_each_entry_safe(...)
        1. cds_list_del_init(&event->list)
        2. glusterd_get_txn_opinfo(&event->txn_id, &txn_op_info)
          event - glusterd_op_sm_event_t
          txn_op_info - glusterd_opinfo_t
        3. opinfo = txn_op_info
          opinfo - 프로세스 전역 스코프
        4. state = glusterd_op_state_table[opinfo.state.state]
          glusterd_op_sm_t -> 상세 분석 필요
        5. handler = state[event_type].handler
        6. handler(event, event->ctx)
          핸들러 종류 파악 필요
        7. glusterd_op_sm_transition_state(&opinfo, state, event_type)
        8. state[event_type].nextstateGD_OP_STATE_DEFAULT이고, event_type이 <cde>GD_OP_EVENT_UNLOCK이면,
          1. glusterd_clear_txn_info(&event->txn_id)
          2. 그 외에는 glusterd_set_txn_opinfo(&event->txn_id, &opinfo)
        9. glusterd_destroy_op_event_ctx(&event)
    3. synclock_unlock(&gd_op_sm_lock)

glusterd_mgmt_v3_initiate_lockdown()

int
glusterd_mgmt_v3_initiate_lockdown(
    glusterd_op_t   op,             #
    dict_t          *dict,          #
    char            **op_errstr,    #
    uint32_t        *op_errno,      #
    gf_boolean_t    *is_acquired,   #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • ret = glusterd_multiple_mgmt_v3_lock(dict, MY_UUID, op_errno)
    로컬 피어(glusterd)의 항목들에 대해 잠금을 수행한다.
  • *is_acquired = _gf_true
    로컬 잠금이 수행되었음을 나타내는 플래그의 포인터 변수인 is_acquiredTRUE(_gf_true)를 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    후술되는 gd_syncargs_init()을 참고
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_lock(op, dict, peerinfo, &args, MY_UUID, peer_uuid)
      해당 노드에 대해 v3 잠금 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_lock()을 참조
    • peer_cnt++
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt)
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.errstr)
            *op_errstr = gf_strdup(args.errstr);
    
    요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.
  • *op_errno = args.op_errno
    요청에 대한 오류 코드인 args.op_errno 멤버의 값을 op_errno 포인터가 가리키는 대상에 복사한다.

glusterd_mgmt_v3_build_payload()

int
glusterd_mgmt_v3_build_payload(
    dict_t          **req,          #
    char            **op_errstr,    #
    dict_t          *dict,          #
    glusterd_op_t   op              #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • req_dict = dict_new();
    사전형(dict_t)의 포인터 변수인 req_dict에 새로운 사전을 할당한다.
  • switch (op) {
    매개 변수로 넘겨 받은 명령 코드(glusterd_op_t) 별로 분기를 수행한다.
    • case GD_OP_MAX_OPVERSION:
      fallthrough
    • case GD_OP_SNAP:
      • dict_copy(dict, req_dict)
        새로 할당한 req_dict에 기존 dict 사전 변수를 복사한다.
    • case GD_OP_START_VOLUME:
      fallthrough
    • case GD_OP_ADD_BRICK:
      fallthrough
    • case GD_OP_REPLACE_BRICK:
      fallthrough
    • case GD_OP_RESET_BRICK:
      fallthrough
    • case GD_OP_ADD_TIER_BRICK:
      • ret = dict_get_str(dict, "volname", &volname)
        dict 사전에 있는 "volname" 키의 값을 volname 변수에 저장한다.
      • if (strcasecmp(volname, "all")) {
        volname 변수가 "all"이 아니라면 분기르 수행한다.
        • ret = glusterd_dict_set_volid(dict, volname, op_errstr)
          volname에 해당하는 볼륨 정보를 찾아서 그 UUID를 dict 사전의 "vol-id"에 복사한다.
          자세한 내용은 glusterd_dict_set_volid() 참조
        • dict_copy(dict, req_dict)
        req_dict에 기존 dict 사전 변수를 복사한다.
    • case GD_OP_TIER_START_STOP:
      fallthrough
    • case GD_OP_REMOVE_TIER_BRICK:
      fallthrough
    • case GD_OP_DETACH_TIER_STATUS:
      fallthrough
    • case GD_OP_TIER_STATUS:
      • dict_copy(dict, req_dict)
        req_dict에 기존 dict 사전 변수를 복사한다.
  • *req = req_dict
    요청 매개 변수로 들어온 req가 가리키는 사전의 포인터 변수가 새로 할당한 req_dict를 가리키도록 한다.
  • ret = 0
    함수의 반환 값을 0으로 저장한다.

glusterd_mgmt_v3_pre_validate()

int
glusterd_mgmt_v3_pre_validate(
    glusterd_op_t   op,             #
    dict_t          *req_dict,      #
    char            **op_errstr,    #
    uint32_t        *op_errno,      #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new()
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • ret = gd_mgmt_v3_pre_validate_fn(op, req_dict, op_errstr, rsp_dict, op_errno)
    로컬 노드에서의 사전-검증을 수행한다.
    자세한 내용은 gd_mgmt_v3_pre_validate_fn() 참조
  • if (op != GD_OP_MAX_OPVERSION) {
    요청 받은 명령이 해당 glusterd(mgmt)가 지원하는 가장 높은 명령 버전을 조회하는 GD_OP_MAX_OPVERSION이 아니라면 분기를 수행한다.
    • ret = glusterd_pre_validate_aggr_rsp_dict(op, req_dict, rsp_dict)
      각 glusterd(mgmt)의 응답 사전인 rsp_dict를 요청 사전인 req_dict에 취합한다.
    • dict_unref(rsp_dict)
      취합되었으므로 필요 없어진 rsp_dict의 참조 계수를 줄인다.
    • rsp_dict = NULL
      rsp_dictNULL로 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_pre_validate_req(op, req_dict, peerinfo, &args, MY_UUID, peer_uuid)
      해당 노드에 대해 v3 사전 검증 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_pre_validate_req() 참조
    • peer_cnt++
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 정상적으로 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt)
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.op_ret) {
            ...;
    
            if (args.errstr)
                *op_errstr = gf_strdup(args.errstr);
    }
    
    사전 검증 요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    사전 검증 요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.
  • *op_errno = args.op_errno
    사전 검증 요청에 대한 오류 코드인 args.op_errno 멤버의 값을 op_errno 포인터가 가리키는 대상에 복사한다.

glusterd_mgmt_v3_brick_op()

int
glusterd_mgmt_v3_brick_op(
    glusterd_op_t   op,             #
    dict_t          *req_dict,      #
    char            **op_errstr,    #
    uint32_t        txn_generation  #
)
  • this = THIS
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new()
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • ret = gd_mgmt_v3_brick_op_fn(op, req_dict, op_errstr, rsp_dict)
    로컬 노드에서의 브릭 명령을 수행한다.
    자세한 내용은 gd_mgmt_v3_brick_op_fn() 참조
  • dist_unref(rsp_dict)
    필요 없어진 rsp_dict를 참조 계수를 줄인다.
  • rsp_dict = NULL
    rsp_dictNULL로 저장한다.
  • gd_syncargs_init(&args, NULL)
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args))
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
    GlusterFS/코드_분석/동기_작업_처리 참조
  • peer_cnt = 0
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock()
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu(peerinfo, &conf->peers, uuid_list)
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
              continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.

glusterd_mgmt_v3_commit()

int
glusterd_mgmt_v3_commit(
        glusterd_op_t   op,
        dict_t         *op_ctx,
        dict_t         *req_dict,
        char          **op_errstr,
        uint32_t       *op_errno,
        uint32_t        txn_generation
)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private;
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new ();
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • /* Commit on local node */
    ret = gd_mgmt_v3_commit_fn (op, req_dict, op_errstr,
                                op_errno, rsp_dict);
    
    로컬 노드에서의 커밋 명령을 수행한다.
    자세한 내용은 gd_mgmt_v3_commit_fn() 참조
  • if (ret) {
            gf_msg (this->name, GF_LOG_ERROR, 0,
                    GD_MSG_COMMIT_OP_FAIL,
                    "Commit failed for "
                    "operation %s on local node",
                    gd_op_list[op]);
    
            if (*op_errstr == NULL) {
                    ret = gf_asprintf (op_errstr,
                                        "Commit failed "
                                        "on localhost. Please "
                                        "check log file for details.");
                    if (ret == -1)
                            *op_errstr = NULL;
    
                    ret = -1;
            }
            goto out;
    }
    
    커밋의 반환 결과가 0이 아니라면 다음과 같은 동작을 한다.
    1. 오류 메시지를 출력
    2. 오류 메시지를 op_errstr에 저장
    3. out: 레이블로 건너 뛰기
  • ret = glusterd_syncop_aggr_rsp_dict (op, op_ctx,
                                            rsp_dict);
    
    응답 사전인 rsp_dict을 명령 컨텍스트 사전인 op_ctx에 병합한다.
    자세한 내용은 glusterd_syncop_aggr_rsp_dict() 참조
  • dict_unref (rsp_dict);
    rsp_dict = NULL;
    
    병합되어 필요 없어진 rsp_dict의 참조를 해제하고, 그 값을 NULL로 저장한다.
  • gd_syncargs_init (&args, op_ctx);
    
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args));
    
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
  • peer_cnt = 0;
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock ();
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu (peerinfo, &conf->peers, uuid_list) {
            /* Only send requests to peers who were available before the
             * transaction started
             */
            ...
    }
    
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected) {
          ...
      }
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이면 분기를 수행한다.
      • if (op == GD_OP_TIER_STATUS || op ==
                        GD_OP_DETACH_TIER_STATUS) {
                ret = dict_get_int32 (args.dict, "count",
                                        &count);
                if (ret)
                        gf_msg (this->name, GF_LOG_ERROR, 0,
                                GD_MSG_DICT_GET_FAILED,
                                "failed to get index");
                count++;
                ret = dict_set_int32 (args.dict, "count",
                                        count);
                if (ret)
                        gf_msg (this->name, GF_LOG_ERROR, 0,
                                GD_MSG_DICT_GET_FAILED,
                                "failed to set index");
        }
        
        명령 코드(op)가 티어링 상태나 탈착 상태를 조회를 위한 GD_OP_TIER_STATUS 혹은 GD_OP_DETACH_TIER_STATUS인 경우에 분기를 수행한다.
        이 분기 내에서는 명령 컨텍스트 사전의 "count" 키의 값을 1만큼 증가시킨다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_commit_req (op, req_dict, peerinfo, &args,
                              MY_UUID, peer_uuid);
      
      해당 노드에 대해 v3 커밋 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_commit_req() 참조
    • peer_cnt++;
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 정상적으로 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt);
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.op_ret) {
            ...;
    
            if (args.errstr)
                *op_errstr = gf_strdup(args.errstr);
    }
    
    사전 검증 요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    사전 검증 요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.
  • *op_errno = args.op_errno
    사전 검증 요청에 대한 오류 코드인 args.op_errno 멤버의 값을 op_errno 포인터가 가리키는 대상에 복사한다.
  • glusterd_op_modify_op_ctx (op, op_ctx);
    CLI에게 응답을 보내기 전에 피어의 UUID를 호스트명으로 바꾸는 등의 편의 작업을 위해 명령 컨텍스트 사전을 수정한다.
    자세한 내용은 glusterd_op_modify_op_ctx() 참조

glusterd_mgmt_v3_post_validate()

int
glusterd_mgmt_v3_post_validate(
        glusterd_op_t   op,
        int32_t         op_ret,
        dict_t         *dict,
        dict_t         *req_dict,
        char          **op_errstr,
        uint32_t        txn_generation
)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private;
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • rsp_dict = dict_new ();
    응답으로 보낼 사전 변수인 rsp_dict를 할당한다.
  • /* Copy the contents of dict like missed snaps info to req_dict */
    if (op != GD_OP_REMOVE_TIER_BRICK)
            /* dict and req_dict has the same values during remove tier
                * brick (detach start) So this rewrite make the remove brick
                * id to become empty.
                * Avoiding to copy it retains the value. */
            dict_copy (dict, req_dict);
    
    명령 코드가 티어링을 구성하는 브릭을 제거하는 요청인 GD_OP_REMOVE_TIER_BRICK이라면, 매개 변수로 넘겨 받은 dict 사전 변수를 req_dict에 복사한다.
    이는 해당 명령을 수행하는 과정에서 제거 대상이 되는 브릭의 식별자가 빈 값이 되어 후처리 과정에서 문제가 발생하기 때문이다.
  • /* Post Validation on local node */
    ret = gd_mgmt_v3_post_validate_fn (op, op_ret, req_dict, op_errstr,
                                    rsp_dict);
    
    로컬 노드에서 사후 검증을 수행한다.
    자세한 내용은 gd_mgmt_v3_post_validate_fn()을 참조
  • if (ret) {
            gf_msg (this->name, GF_LOG_ERROR, 0,
                    GD_MSG_POST_VALIDATION_FAIL,
                    "Post Validation failed for "
                    "operation %s on local node",
                    gd_op_list[op]);
    
            if (*op_errstr == NULL) {
                    ret = gf_asprintf (op_errstr,
                                        "Post-validation failed "
                                        "on localhost. Please check "
                                        "log file for details");
                    if (ret == -1)
                            *op_errstr = NULL;
    
                    ret = -1;
            }
            goto out;
    }
    
  • dict_unref (rsp_dict);
    rsp_dict = NULL;
    
    병합되어 필요 없어진 rsp_dict의 참조를 해제하고, 그 값을 NULL로 저장한다.
  • gd_syncargs_init (&args, op_ctx);
    
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args));
    
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
  • peer_cnt = 0;
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock ();
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu (peerinfo, &conf->peers, uuid_list) {
            /* Only send requests to peers who were available before the
             * transaction started
             */
            ...
    }
    
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
          continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_post_validate_req (op, op_ret, req_dict, peerinfo,
                                    &args, MY_UUID, peer_uuid);
      
      해당 노드에 대해 v3 사후 검증 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_post_validate_req() 참조
    • peer_cnt++;
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 정상적으로 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt);
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.op_ret) {
            ...;
    
            if (args.errstr)
                *op_errstr = gf_strdup(args.errstr);
    }
    
    사전 검증 요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    사전 검증 요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.


glusterd_mgmt_v3_release_peer_locks()

int
glusterd_mgmt_v3_release_peer_locks(
        glusterd_op_t   op,
        dict_t         *dict,
        int32_t         op_ret,
        char          **op_errstr,
        gf_boolean_t    is_acquired,
        uint32_t        txn_generation
)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private;
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • /* If the lock has not been held during this
     * transaction, do not send unlock requests */
    if (!is_acquired)
            goto out;
    
    트랜잭션 동안에 잠금 처리가 되지 않았었다면 바로 out: 레이블로 건너 뛴다.
  • gd_syncargs_init (&args, op_ctx);
    
    클러스터의 모든 피어에 대한 요청을 동기적으로 요청하기 위해 args 동기 작업 변수(struct syncargs)를 초기화한다.
    자세한 내용은 gd_syncargs_init()을 참조
  • synctask_barrier_init((&args));
    
    GlusterFS/코드_분석/동기_작업_처리/syncbarrier_init() 함수의 매크로 함수인 synctask_barrier_init()을 호출하여 동기 작업에 대한 배리어를 초기화한다.
  • peer_cnt = 0;
    정상적으로 요청된 피어의 수를 나타내는 peer_cnt 변수의 값을 0으로 저장한다.
  • rcu_read_lock ();
    CDS(Concurrent Data Structures) 리스트로 이루어진 피어 목록을 순회하기에 앞서 RCU 읽기 잠금을 수행한다.
  • cds_list_for_each_entry_rcu (peerinfo, &conf->peers, uuid_list) {
            /* Only send requests to peers who were available before the
             * transaction started
             */
            ...
    }
    
    설정 정보의 피어 목록을 peerinfo에 하나씩 저장하면서 순회한다.
    • if (peerinfo->generation > txn_generation)
              continue;
      
      피어 정보의 세대 번호가 트랜잭션 세대 번호보다 크다면 정보가 갱신된 것이므로 건너뛴다.
    • if (!peerinfo->connected)
          continue;
      
      피어 정보의 연결 상태를 나타내는 connected 멤버가 FALSE라면 연결되지 않은 상태이므로 건너뛴다.
    • if (op != GD_OP_SYNC_VOLUME &&
          peerinfo->state.state != GD_FRIEND_STATE_BEFRIENDED)
              continue;
      
      요청된 명령 코드가 GD_OP_SYNC_VOLUME이 아니고, 피어 상태 기계의 현재 상태가 클러스터에 정상 참여 중임을 나타내는 GD_FRIEND_STATE_BEFRIENDED가 아니라면 건너뛴다.
    • gd_mgmt_v3_unlock (op, dict, peerinfo, &args,
                         MY_UUID, peer_uuid);
      
      해당 노드에 대해 v3 잠금 해제 프로토콜을 요청한다.
      자세한 내용은 gd_mgmt_v3_unlock() 참조
    • peer_cnt++;
      정상적으로 요청된 노드의 개수를 1만큼 증가시킨다.
  • rcu_read_unlock()
    순회에 앞서 잠궜던 RCU 읽기 잠금을 해제한다.
  • if (0 == peer_cnt) {
            ret = 0;
            goto out;
    }
    
    요청을 정상적으로 수행한 노드가 없다면 반환값(ret)을 0으로 저장하고 out: 레이블로 점프하여 정상 처리한다.
  • gd_synctask_barrier_wait((&args), peer_cnt);
    앞서 수행한 배리어가 해제될 때까지 대기한다.
    자세한 내용은 gd_synctask_barrier_wait() 참조
  • if (args.op_ret) {
            ...;
    
            if (args.errstr)
                *op_errstr = gf_strdup(args.errstr);
    }
    
    사전 검증 요청에 대한 오류 메시지인 args.errstr 멤버가 NULL이 아니라면 오류가 발생한 것이므로 op_errstr이 가리키는 메모리에 args.errstr의 오류 메시지를 복사한다.
  • ret = args.op_ret
    사전 검증 요청에 대한 명령 반환값인 args.op_ret 멤버의 값을 ret에 복사한다.

glusterd_multiple_mgmt_v3_unlock()

int32_t
glusterd_multiple_mgmt_v3_unlock (dict_t *dict, uuid_t uuid)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • if (!dict) {
            gf_msg (this->name, GF_LOG_ERROR, 0,
                    GD_MSG_DICT_EMPTY, "dict is null.");
            ret = -1;
            goto out;
    }
    
    매개 변수로 넘겨 받은 dict 변수가 NULL이 아니라면 이 함수의 반환값으로 사용될 ret의 값을 -1로 저장하고 out: 레이블로 건너 뛴다.
  • for (i = 0; valid_types[i].type; i++) {
            ret = glusterd_mgmt_v3_unlock_entity
                                        (dict, uuid,
                                            valid_types[i].type,
                                            valid_types[i].default_value);
            if (ret) {
                    gf_msg (this->name, GF_LOG_ERROR, 0,
                            GD_MSG_MULTIPLE_LOCK_RELEASE_FAIL,
                            "Unable to unlock all %s",
                            valid_types[i].type);
                    op_ret = ret;
            }
    }
    
    v3 프로토콜의 잠금 유형을 나타내는 valid_types 구조체 배열을 순회하며 각 잠금 유형에 대해 glusterd_mgmt_v3_unlock_entity()를 호출하여 잠금을 해제한다.
  •         ret = op_ret;
    out:
            gf_msg_debug (this->name, 0, "Returning %d", ret);
            return ret;
    
    이 함수의 반환값을 저장하는 ret 변수에 op_ret의 값을 복사하고, 이를 반환한다.

glusterd_op_send_cli_response()

int32_t
glusterd_op_send_cli_response(
        glusterd_op_t    op,
        int32_t          op_ret,
        int32_t          op_errno,
        rpcsvc_request_t *req,
        void             *op_ctx,
        char             *op_errstr
)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • conf = this->private;
    현재 xlator의 개별 데이터(this->private)에 기록된 현재 설정 정보(glusterd_conf_t)를 얻는다.
  • ctx = op_ctx;
    ctx 변수가 명령 컨텍스트 사전인 op_ctx와 같은 메모리를 가리키도록 한다.
  • switch (op) {
            ...
    }
    
    명령 코드를 나타내는 op의 값에 따라 분기를 수행한다.
    • case GD_OP_DETACH_TIER:
      case GD_OP_REMOVE_TIER_BRICK:
      case GD_OP_REMOVE_BRICK:
      {
              if (ctx)
                      ret = dict_get_str (ctx, "errstr", &errstr);
              break;
      }
      
      GD_OP_DETACH_TIER, GD_OP_REMOVE_TIER_BRICK, GD_OP_REMOVE_BRICK 명령의 경우, 컨텍스트 사전의 "errstr" 키에 해당하는 값을 errstr에 복사한다.
    • case GD_OP_RESET_VOLUME:
      {
          if (op_ret && !op_errstr)
                  errstr = "Error while resetting options";
          break;
      }
      
      GD_OP_RESET_VOLUME 명령의 경우, 명령의 반환 값인 op_ret가 0이 아니고, 그 오류 메시지를 기록하는 op_errstr 변수가 NULL인 경우에 한하여(즉, 오류가 발생했으나 오류 메시지가 없음) CLI 요청에 대한 응답으로 보내질 오류 메시지를 기록하는 변수인 errstr에 설정 중에 오류가 발생했음을 알리는 오류 메시지를 저장한다.
    • case GD_OP_TIER_MIGRATE:
      case GD_OP_TIER_STATUS:
      case GD_OP_DETACH_TIER_STATUS:
      case GD_OP_REBALANCE:
      case GD_OP_DEFRAG_BRICK_VOLUME:
      {
              if (ctx) {
                      ret = dict_get_int32 (ctx, "status", &status);
                      if (ret) {
                              gf_msg_trace (this->name, 0,
                                      "failed to get status");
                      }
              }
              break;
      }
      
      GD_OP_TIER_MIGRATE, GD_OP_TIER_STATUS, GD_OP_DETACH_TIER_STATUS, GD_OP_REBALANCE, GD_OP_DEFRAG_BRICK_VOLUME 명령의 경우, 컨텍스트가 NULL이 아니라면 컨텍스트의 "status" 키에 대한 값을 status에 저장한다.
      이 값은 해당 함수의 뒷부분에서 CLI 응답에 상태 정보를 실어보낼 때 사용된다.
    • case GD_OP_GSYNC_CREATE:
      case GD_OP_GSYNC_SET:
      {
              if (ctx) {
                      ret = dict_get_str (ctx, "errstr", &errstr);
                      ret = dict_set_str (ctx, "glusterd_workdir", conf->workdir);
                      /* swallow error here, that will be re-triggered in cli */
      
              }
              break;
      
      }
      
      GD_OP_GSYNC_SET, GD_OP_GSYNC_CREATE 명령이고, 컨텍스트가 NULL이 아니라면
      • 컨텍스트의 "errstr" 키에 대한 값을 CLI 요청에 대한 응답으로 보내질 오류 메시지를 기록하는 변수인 errstr에 저장한다.
      • 컨텍스트의 "glusterd_workdir" 키에 대한 값을 glusterd(mgmt)의 workdir 설정 값으로 저장한다.
    • case GD_OP_PROFILE_VOLUME:
      {
              if (ctx && dict_get_int32 (ctx, "count", &count)) {
                      ret = dict_set_int32 (ctx, "count", 0);
                      if (ret) {
                              gf_msg (this->name, GF_LOG_ERROR, 0,
                                      GD_MSG_DICT_SET_FAILED,
                                      "failed to set count in dictionary");
                      }
              }
              break;
      }
      
      GD_OP_PROFILE_VOLUME 명령이고, 컨텍스트가 NULL이 아니며, 컨텍스트의 "count" 키에 대한 값이 0이 아닌 경우
      • 컨텍스트의 "count" 키에 대한 값을 0으로 설정한다.
    • case GD_OP_START_BRICK:
      case GD_OP_STOP_BRICK:
      {
              gf_msg_debug (this->name, 0, "op '%s' not supported",
                      gd_op_list[op]);
              break;
      }
      
      GD_OP_START_BRICK, GD_OP_STOP_BRICK 명령인 경우
      • 지원되지 않는 명령임을 알리는 디버그 메시지를 출력한다. (단일 브릭 수준의 시작/중지는 미지원)
    • case GD_OP_NONE:
      case GD_OP_MAX:
      {
              gf_msg (this->name, GF_LOG_ERROR, EINVAL,
                      GD_MSG_OP_UNSUPPORTED, "invalid operation");
              break;
      }
      
      GD_OP_NONE, GD_OP_MAX 명령인 경우
      • 유효하지 않은 명령임을 알리는 메시지를 출력한다.
    • case GD_OP_CREATE_VOLUME:
      case GD_OP_START_VOLUME:
      case GD_OP_STOP_VOLUME:
      case GD_OP_DELETE_VOLUME:
      case GD_OP_DEFRAG_VOLUME:
      case GD_OP_ADD_BRICK:
      case GD_OP_LOG_ROTATE:
      case GD_OP_SYNC_VOLUME:
      case GD_OP_STATEDUMP_VOLUME:
      case GD_OP_REPLACE_BRICK:
      case GD_OP_STATUS_VOLUME:
      case GD_OP_SET_VOLUME:
      case GD_OP_LIST_VOLUME:
      case GD_OP_CLEARLOCKS_VOLUME:
      case GD_OP_HEAL_VOLUME:
      case GD_OP_QUOTA:
      case GD_OP_SNAP:
      case GD_OP_BARRIER:
      case GD_OP_BITROT:
      case GD_OP_SCRUB_STATUS:
      case GD_OP_SCRUB_ONDEMAND:
      case GD_OP_RESET_BRICK:
      case GD_OP_MAX_OPVERSION:
      case GD_OP_TIER_START_STOP:
      case GD_OP_DETACH_NOT_STARTED:
      case GD_OP_GANESHA:
      case GD_OP_ADD_TIER_BRICK:
      
      {
              /*nothing specific to be done*/
              break;
      }
      
      상기 명령들에 대해서는 아무 것도 수행하지 않는다.
    • case GD_OP_COPY_FILE:
      {
              if (ctx)
                      ret = dict_get_str (ctx, "errstr", &errstr);
              break;
      }
      
      GD_OP_COPY_FILE 명령이고, 컨텍스트가 NULL이 아닌 경우
      • 컨텍스트의 "errstr" 키에 대한 값을 errstr에 저장한다.
    • case GD_OP_SYS_EXEC:
      {
              if (ctx) {
                      ret = dict_get_str (ctx, "errstr", &errstr);
                      ret = dict_set_str (ctx, "glusterd_workdir",
                                          conf->workdir);
              }
              break;
      }
      
      GD_OP_SYS_EXEC 명령이고, 컨텍스트가 NULL이 아닌 경우
      • 컨텍스트의 "errstr" 키에 대한 값을 errstr에 저장한다.
      • 컨텍스트의 "glusterd_workdir" 키에 대한 값을 glusterd(mgmt)의 workdir 설정 값으로 저장한다.
  • rsp.op_ret = op_ret;
    rsp.op_errno = op_errno;
    
    CLI 요청에 대한 응답 코드인 op_ret 변수를 응답으로 보내질 rspop_ret 멤버에 복사한다.
    CLI 요청에 대한 오류 번호인 op_errno 변수를 응답으로 보내질 rspop_errno 멤버에 복사한다.
  • if (errstr)
            rsp.op_errstr = errstr;
    else if (op_errstr)
            rsp.op_errstr = op_errstr;
    
    if (!rsp.op_errstr)
            rsp.op_errstr = "";
    
    응답으로 보내질 오류 메시지를 기록하는 rsp.op_errstr가 적절한 오류 메시지를 가리키도록 한다.
    errstrNULL이 아니라면 이를 가리키도록하고, errstrNULL인 대신에 op_errstrNULL이 아니라면 이를 가리키도록 한다.
  • if (ctx) {
            ret = dict_allocate_and_serialize (ctx, &rsp.dict.dict_val,
                                                &rsp.dict.dict_len);
            if (ret < 0 )
                    gf_msg (this->name, GF_LOG_ERROR, 0,
                            GD_MSG_DICT_SERL_LENGTH_GET_FAIL, "failed to "
                            "serialize buffer");
            else
                    free_ptr = rsp.dict.dict_val;
    }
    
    컨텍스트가 NULL이 아니라면 컨텍스트를 직렬화하여 CLI 응답 사전(rsp.dict)에 저장한다.
    정상적으로 저장했다면 이 함수의 반환 직전에 할당 해제할 free_ptr 변수가 직렬화된 응답 사전이 저장된 메모리(rsp.dict.dict_val)를 가리키도록 한다.
  • /* needed by 'rebalance status' */
    if (status)
            rsp.op_errno = status;
    
    앞서 기록한 status 변수의 값이 0이 아니라면 CLI 응답의 오류 코드(rsp.op_errno)에 이를 복사한다.
  • cli_rsp = &rsp;
    xdrproc = (xdrproc_t) xdr_gf_cli_rsp;
    
    CLI로 보낼 응답을 가리키는 cli_rsp 변수가 rsp의 메모리를 가리키도록 하고, 이 응답을 XDR로 변환하기 위해 사용되는 함수를 가리키는 xdrproc 함수 포인터가 xdr_gf_cli_rsp()를 가리키도록 한다.
  • glusterd_to_cli (req, cli_rsp, NULL, 0, NULL,
                        xdrproc, ctx);
    
    CLI 요청 자료, 응답 자료, XDR 변환 함수, 컨텍스트를 매개 변수로 하는 glusterd_to_cli()를 호출하여 요청을 송신한 CLI에게 응답한다.

glusterd_to_cli()

glusterd_to_cli()

int
glusterd_to_cli (
        rpcsvc_request_t *req,
        gf_cli_rsp       *arg,
        struct iovec     *payload,
        int               payloadcount,
        struct iobref    *iobref,
        xdrproc_t         xdrproc,
        dict_t           *dict
)
  • this = THIS;
    현재 xlator(여기에서는 mgmt)를 가리키는 this 변수에 THIS 매크로 함수의 반환을 저장한다.
  • op_ret = arg->op_ret;
    op_errstr = arg->op_errstr;
    
    매개 변수로 넘겨 받은 CLI 응답 자료 구조인 arg의 명령 결과값(arg->op_ret과 오류 메시지(arg->op_errstr)를 각각 op_ret, op_errstr에 복사한다.
  • ret = dict_get_str (dict, "cmd-str", &cmd);
    
    매개 변수로 넘겨 받은 컨텍스트 사전(dict)에서 "cmd-str" 키의 값을 cmd 변수로 가져온다.
  • glusterd_submit_reply (req, arg, payload, payloadcount, iobref,
                           (xdrproc_t) xdrproc);
    
    glusterd_submit_reply() 함수를 호출하여 CLI 요청에 대한 응답을 RPC를 통해 송신한다.
  • if (dict) {
            dict_unref (dict);
    }
    
    응답을 송신한 이후 필요 없어진 컨텍스트 사전(dict)에 대한 참조를 해제한다.
  • return ret;
    "cmd-str" 키의 값을 가져올 때의 dict_get_str() 함수의 반환값을 이 함수의 반환값으로서 반환한다.

glusterd_submit_reply()

int
glusterd_submit_reply (
        rpcsvc_request_t *req,
        void             *arg,
        struct iovec     *payload,
        int               payloadcount,
        struct iobref    *iobref,
        xdrproc_t         xdrproc
)
  • if (!req) {
            GF_ASSERT (req);
            goto out;
    }
    
    매개 변수로 넘겨 받은 RPC 서비스 요청 자료 구조(rpcsvc_request_t)인 reqNULL이라면 즉시 반환을 위해 out: 레이블로 건너 뛴다.
  • if (!iobref) {
            iobref = iobref_new ();
            if (!iobref) {
                    gf_msg ("glusterd", GF_LOG_ERROR, ENOMEM,
                            GD_MSG_NO_MEMORY, "out of memory");
                    goto out;
            }
    
            new_iobref = 1;
    }
    
    매개 변수로 넘겨 받은 다중 입출력 버퍼 자료 구조(struct iobref)인 iobrefNULL이라면 iobref_new() 함수를 통해 새로운 다중 입출력 버퍼를 만들어 iobref가 이를 가리키게 한다.
    또한 이후의 코드에서, 새로운 입출력 버퍼를 할당했음을 알기 위해 new_iobref 플래그를 1로 기록한다.
  • iob = glusterd_serialize_reply (req, arg, &rsp, xdrproc);
    if (!iob) {
            gf_msg ("glusterd", GF_LOG_ERROR, 0,
                    GD_MSG_SERIALIZE_MSG_FAIL, "Failed to serialize reply");
    } else {
            iobref_add (iobref, iob);
    }
    
    glusterd_serialize_reply() 함수를 통해 CLI 응답을 직렬화하고, 입출력 버퍼 자료 구조(struct iobuf)인 iob가 이 직렬화된 응답이 저장된 메모리를 가리키도록 한다.
    만일 할당이 실패했다면 오류 메시지를 출력하고, 성공했다면 iobref_add() 함수를 통해 iob를 다중 입출력 버퍼인 iob에 추가한다.
  • ret = rpcsvc_submit_generic (req, &rsp, 1, payload, payloadcount,
                                 iobref);
    
    rpcsvc_submit_generic() 함수를 통해 CLI에게 응답을 송신한다.
  • /* Now that we've done our job of handing the message to the RPC layer
        * we can safely unref the iob in the hope that RPC layer must have
        * ref'ed the iob on receiving into the txlist.
        */
    if (ret == -1) {
            gf_msg ("glusterd", GF_LOG_ERROR, 0,
                    GD_MSG_REPLY_SUBMIT_FAIL, "Reply submission failed");
            goto out;
    }
    
    ret = 0;
    
    rpcsvc_submit_generic()이 오류가 발생했다면(반환값이 -1) 오류 메시지를 출력하고 즉시 반환을 위해 out: 레이블로 건너 뛴다.
  • out:
    
            if (new_iobref) {
                    iobref_unref (iobref);
            }
    
            if (iob)
                    iobuf_unref (iob);
            return ret;
    
    new_iobref가 0이 아니라면 이 함수 내에서 새로운 다중 입출력 버퍼의 할당이 이루어진 것이므로, iobref에 대해 iobref_unref()를 호출하여 참조를 해제한다.
    또한 iobuf_unref()를 호출하여 직렬화된 CLI 응답을 가리키고 있는 iob를 할당 해제한다.

glusterd_serialize_reply()

struct iobuf *
glusterd_serialize_reply (
        rpcsvc_request_t *req,
        void             *arg,
        struct iovec     *outmsg,
        xdrproc_t         xdrproc
)
  •         /* First, get the io buffer into which the reply in arg will
             * be serialized.
             */
            rsp_size = xdr_sizeof (xdrproc, arg);
            iob = iobuf_get2 (req->svc->ctx->iobuf_pool, rsp_size);
            if (!iob) {
                    gf_msg ("glusterd", GF_LOG_ERROR, ENOMEM,
                            GD_MSG_NO_MEMORY,
                            "Failed to get iobuf");
                    goto ret;
            }
    
            iobuf_to_iovec (iob, outmsg);
    
    매개 변수로 넘겨 받은 XDR 변환 함수(xdrproc)와 직렬화할 CLI 응답 데이터(arg)를 매개 변수로 하는 xdr_sizeof() 함수를 호출한 뒤에 그 반환값인 응답 데이터의 길이를 rsp_size 변수에 저장한다.
    이어서 iobuf_get2() 함수를 호출하여 rsp_size 변수가 저장하고 있는 응답 데이터 길이에 알맞는 입출력 버퍼를 전역 입출력 버퍼 풀에서 할당 받는다.
    입출력 버퍼를 할당 받지 못했다면 오류 메시지를 출력하고 ret: 레이블로 건너 뛰며, 할당을 받았다면 그 버퍼와 입출력 벡터인 outmsg를 매개 변수로 하는 iobuf_to_iovec() 함수를 호출하여 입출력 벡터가 할당 받은 입출력 버퍼를 가리키도록함으로써 입출력 버퍼를 입출력 벡터로 변환한다.
  •         /* Use the given serializer to translate the give C structure in arg
             * to XDR format which will be written into the buffer in outmsg.
             */
            /* retlen is used to received the error since size_t is unsigned and we
             * need -1 for error notification during encoding.
             */
            retlen = xdr_serialize_generic (*outmsg, arg, xdrproc);
            if (retlen == -1) {
                    gf_msg ("glusterd", GF_LOG_ERROR, 0,
                            GD_MSG_ENCODE_FAIL, "Failed to encode message");
                    goto ret;
            }
    
            outmsg->iov_len = retlen;
    
    변환된 입출력 벡터(outmsg)와 직렬화할 응답 데이터(arg), XDR 변환 함수를 매개 변수로 하는 xdr_serialize_generic() 함수를 호출하여 XDR 변환을 수행하고 그 반환값인 직렬화된 데이터의 길이를 retlen 변수에 저장한다.
    변환에서 오류가 발생했다면(즉, retlen == -1) 오류 메시지를 출력하고 ret: 레이블로 건너 뛴다.
    변환이 완료되었다면 변환된 입출력 벡터의 길이인 retlen의 값을 그 벡터의 길이로 저장한다.
  • ret:
            if (retlen == -1) {
                    iobuf_unref (iob);
                    iob = NULL;
            }
    
            return iob;
    
    변환에서 오류가 발생했다면 할당된 입출력 버퍼를 할당 해제하고 iob를 반환한다.

같이 보기

상태 기계에 대한 질의

14:56:56 P<PotatoGim> Ji-Hyeon Gim atinm: Could you give me any tip for understanding workflow of 'state-machine' in mgmt-xlator?
14:58:37 P<PotatoGim> Ji-Hyeon Gim like images or docs :)
18:09:10 A<atinm> Atin Mukherjee PotatoGim, we have two state machines in glusterd, 1. op-sm which is for volume transactions & 2. peer-sm which is for peer handshaking, which one are you referring at?
18:10:40 P<PotatoGim> Ji-Hyeon Gim atinm: I want to understand both :)
18:12:55 P<PotatoGim> Ji-Hyeon Gim If I can do that  😃 
18:17:32 P<PotatoGim> Ji-Hyeon Gim Could you let me know what does 'ac'(e.g. "glusterd_ac_*") actually mean?
18:23:07 A<atinm> Atin Mukherjee PotatoGim, give me some time, one of our team members came up with a state diagram which I can share in gluster-dev ML
18:24:19 K<•kshlm> Kaushal PotatoGim, https://drive.google.com/open?id=0B6dvOu916HEzUFFoNmFiYWhhU28
18:24:28 P<PotatoGim> Ji-Hyeon Gim atinm: Wow! I really appreciate your help!
18:24:51 K<•kshlm> Kaushal That's a messy graphviz graph I did sometime back of the peer state machine.
18:25:27 The circles are the states, and the arrows are the transitions.
18:25:38 The labels on the arrorws are the events.
18:25:54 This diagram doesn't have the actions performed when the even occurs.
18:26:05 P<PotatoGim> Ji-Hyeon Gim kshlm: Thank you very much! :)
18:26:19 A<atinm> Atin Mukherjee PotatoGim, this is friend-sm BTW
18:26:50 K<•kshlm> Kaushal The glusterd_ac_* functions are the actions that happen on an event.
18:28:04 Which action happens for an event when in a particular state, and what state you move to after the action is defined by the state machine tables.
18:28:21 P<PotatoGim> Ji-Hyeon Gim atinm: It served me a good turn :) If images/docs for op-sm does not exists, I will follow source code :)
18:28:23 K<•kshlm> Kaushal These are available in glusterd-sm.c and glusterd-op-sm.c
18:28:53 A<atinm> Atin Mukherjee PotatoGim, op-sm is much simpler, if you have any questions you can ping kshlm or me on that
18:30:19 K<•kshlm> Kaushal The tables are glusterd_friend_state_table and glusterd_op_state_table respectively.
18:31:09 P<PotatoGim> Ji-Hyeon Gim kshlm, atinm: Thanks you both for your time :)

GD_OP_STATE_ACK_DRAIN 상태에 대한 질의

1:22:03 PM <PotatoGim> Ji-Hyeon Gim What is the meaning of "GD_OP_STATE_ACK_DRAIN" state?
2:49:03 PM <kshlm> Kaushal PotatoGim, That is the state glusterd_op_state_machine enters when it recieves a failure from one of the other nodes. In this state it will wait for any other ACKs or NACKs from the remaining node, and once all replies are recieved, it fails the operation.

이 상태는 다른 노드들로부터 요청에 대한 실패를 받았을 때 진입되는 명령 상태 기계 상태다. 이 상태일 때에는 다른 노드들로부터 ACK 혹은 NACK을 수신하기를 대기하며, 모든 응답을 수신한 후에 명령을 실패한 것으로 처리한다.