SPDK iSCSIターゲットの初期化処理

投稿者: | 2022年7月14日

ターゲットノードを作成した場合、rpc_iscsi_create_target_nodeが呼び出される。

static void
rpc_iscsi_create_target_node(struct spdk_jsonrpc_request *request,
                 const struct spdk_json_val *params)
{
    struct rpc_target_node req = {};
    struct spdk_iscsi_tgt_node *target;
    int32_t pg_tags[MAX_TARGET_MAP] = {0}, ig_tags[MAX_TARGET_MAP] = {0};
    char *bdev_names[RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN] = {0};
    int32_t lun_ids[RPC_ISCSI_CREATE_TARGET_NODE_MAX_LUN] = {0};
    size_t i;

    if (spdk_json_decode_object(params, rpc_target_node_decoders,
                    SPDK_COUNTOF(rpc_target_node_decoders),
                    &req)) {
        SPDK_ERRLOG("spdk_json_decode_object failed\n");
        goto invalid;
    }

    ★引数でわたされたportal groupとinitiator groupのindexを記録。
    for (i = 0; i < req.pg_ig_maps.num_maps; i++) {
        pg_tags[i] = req.pg_ig_maps.maps[i].pg_tag;
        ig_tags[i] = req.pg_ig_maps.maps[i].ig_tag;
    }

    for (i = 0; i < req.luns.num_luns; i++) {
        bdev_names[i] = req.luns.luns[i].bdev_name;
        lun_ids[i] = req.luns.luns[i].lun_id;
    }

    /*
     * Use default parameters in a few places:
     *  index = -1 : automatically pick an index for the new target node
     *  alias = NULL
     */
  ★tgt_node構成の本体
    target = iscsi_tgt_node_construct(-1, req.name, req.alias_name,
                      pg_tags,
                      ig_tags,
                      req.pg_ig_maps.num_maps,
                      (const char **)bdev_names,
                      lun_ids,
                      req.luns.num_luns,
                      req.queue_depth,
                      req.disable_chap,
                      req.require_chap,
                      req.mutual_chap,
                      req.chap_group,
                      req.header_digest,
                      req.data_digest);

    if (target == NULL) {
        goto invalid;
    }

    free_rpc_target_node(&req);

    spdk_jsonrpc_send_bool_response(request, true);
    return;

invalid:
    spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, "Invalid parameters");
    free_rpc_target_node(&req);
}
SPDK_RPC_REGISTER("iscsi_create_target_node", rpc_iscsi_create_target_node, SPDK_RPC_RUNTIME

targetごとにpgのリストを持つ。

struct spdk_iscsi_tgt_node *iscsi_tgt_node_construct(int target_index,
        const char *name, const char *alias,
        int *pg_tag_list, int *ig_tag_list, uint16_t num_maps,
        const char *bdev_name_list[], int *lun_id_list, int num_luns,
        int queue_depth,
        bool disable_chap, bool require_chap, bool mutual_chap, int chap_group,
        bool header_digest, bool data_digest)
{
    char                fullname[MAX_TMPBUF];
    struct spdk_iscsi_tgt_node  *target;
    int             rc;

    if (!iscsi_check_chap_params(disable_chap, require_chap,
                     mutual_chap, chap_group)) {
        return NULL;
    }

    if (num_maps == 0) {
        SPDK_ERRLOG("num_maps = 0\n");
        return NULL;
    }

    if (name == NULL) {
        SPDK_ERRLOG("TargetName not found\n");
        return NULL;
    }

  ★ターゲットの名前✓
    if (strncasecmp(name, "iqn.", 4) != 0
        && strncasecmp(name, "eui.", 4) != 0
        && strncasecmp(name, "naa.", 4) != 0) {
        snprintf(fullname, sizeof(fullname), "%s:%s", g_iscsi.nodebase, name);
    } else {
        snprintf(fullname, sizeof(fullname), "%s", name);
    }

    if (check_iscsi_name(fullname) != 0) {
        SPDK_ERRLOG("TargetName %s contains an invalid character or format.\n",
                name);
        return NULL;
    }

    target = calloc(1, sizeof(*target));
    if (!target) {
        SPDK_ERRLOG("could not allocate target\n");
        return NULL;
    }

    rc = pthread_mutex_init(&target->mutex, NULL);
    if (rc != 0) {
        SPDK_ERRLOG("tgt_node%d: mutex_init() failed\n", target->num);
        iscsi_tgt_node_destruct(target, NULL, NULL);
        return NULL;
    }

    target->num = target_index;

    memcpy(target->name, fullname, strlen(fullname));

    if (alias != NULL) {
        if (strlen(alias) > MAX_TARGET_NAME) {
            iscsi_tgt_node_destruct(target, NULL, NULL);
            return NULL;
        }
        memcpy(target->alias, alias, strlen(alias));
    }

    target->dev = spdk_scsi_dev_construct(fullname, bdev_name_list, lun_id_list, num_luns,
                          SPDK_SPC_PROTOCOL_IDENTIFIER_ISCSI, NULL, NULL);
    if (!target->dev) {
        SPDK_ERRLOG("Could not construct SCSI device\n");
        iscsi_tgt_node_destruct(target, NULL, NULL);
        return NULL;
    }

  ★targetごとにpg_map_headを持つ
    TAILQ_INIT(&target->pg_map_head);


  ★ここでtargetとportal group、initiator groupの対応付けをする。
    rc = iscsi_target_node_add_pg_ig_maps(target, pg_tag_list,
                          ig_tag_list, num_maps);
    if (rc != 0) {
        SPDK_ERRLOG("could not add map to target\n");
        iscsi_tgt_node_destruct(target, NULL, NULL);
        return NULL;
    }

    target->disable_chap = disable_chap;
    target->require_chap = require_chap;
    target->mutual_chap = mutual_chap;
    target->chap_group = chap_group;
    target->header_digest = header_digest;
    target->data_digest = data_digest;

    if (queue_depth > 0 && ((uint32_t)queue_depth <= g_iscsi.MaxQueueDepth)) {
        target->queue_depth = queue_depth;
    } else {
        SPDK_DEBUGLOG(iscsi, "QueueDepth %d is invalid and %d is used instead.\n",
                  queue_depth, g_iscsi.MaxQueueDepth);
        target->queue_depth = g_iscsi.MaxQueueDepth;
    }

  ★ここでtargetを登録
    rc = iscsi_tgt_node_register(target);
    if (rc != 0) {
        SPDK_ERRLOG("register target is failed\n");
        iscsi_tgt_node_destruct(target, NULL, NULL);
        return NULL;
    }

    return target;
}

int
iscsi_target_node_add_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
int *pg_tag_list, int *ig_tag_list, uint16_t num_maps)
{
uint16_t i;
int rc;

pthread_mutex_lock(&g_iscsi.mutex);
for (i = 0; i < num_maps; i++) {
    rc = iscsi_tgt_node_add_pg_ig_map(target, pg_tag_list[i],
                      ig_tag_list[i]);
    if (rc != 0) {
        SPDK_ERRLOG("could not add map to target\n");
        goto invalid;
    }
}
pthread_mutex_unlock(&g_iscsi.mutex);
return 0;

int
iscsi_target_node_add_pg_ig_maps(struct spdk_iscsi_tgt_node *target,
                 int *pg_tag_list, int *ig_tag_list, uint16_t num_maps)
{
    uint16_t i;
    int rc;

    pthread_mutex_lock(&g_iscsi.mutex);
    for (i = 0; i < num_maps; i++) {
    ★ここでターゲットにpgとigのマップを追加
        rc = iscsi_tgt_node_add_pg_ig_map(target, pg_tag_list[i],
                          ig_tag_list[i]);
        if (rc != 0) {
            SPDK_ERRLOG("could not add map to target\n");
            goto invalid;
        }
    }
    pthread_mutex_unlock(&g_iscsi.mutex);
    return 0;

invalid:
    for (; i > 0; --i) {
        iscsi_tgt_node_delete_pg_ig_map(target, pg_tag_list[i - 1],
                        ig_tag_list[i - 1]);
    }
    pthread_mutex_unlock(&g_iscsi.mutex);
    return -1;
}

scsi_tgt_node_add_pg_ig_map(struct spdk_iscsi_tgt_node *target,
                 int pg_tag, int ig_tag)
{
    struct spdk_iscsi_portal_grp    *pg;
    struct spdk_iscsi_pg_map    *pg_map;
    struct spdk_iscsi_init_grp  *ig;
    struct spdk_iscsi_ig_map    *ig_map;
    bool                new_pg_map = false;

    pg = iscsi_portal_grp_find_by_tag(pg_tag);
    if (pg == NULL) {
        SPDK_ERRLOG("%s: PortalGroup%d not found\n", target->name, pg_tag);
        return -ENOENT;
    }
    ig = iscsi_init_grp_find_by_tag(ig_tag);
    if (ig == NULL) {
        SPDK_ERRLOG("%s: InitiatorGroup%d not found\n", target->name, ig_tag);
        return -ENOENT;
    }

    /* get existing pg_map or create new pg_map and add it to target */
    pg_map = iscsi_tgt_node_find_pg_map(target, pg);
    if (pg_map == NULL) {
        pg_map = iscsi_tgt_node_add_pg_map(target, pg);
        if (pg_map == NULL) {
            goto failed;
        }
        new_pg_map = true;
    }

    /* create new ig_map and add it to pg_map */
    ig_map = iscsi_pg_map_add_ig_map(pg_map, ig);
    if (ig_map == NULL) {
        goto failed;
    }

    return 0;

failed:
    if (new_pg_map) {
        _iscsi_tgt_node_delete_pg_map(target, pg_map);
    }

    return -1;
}

PDU処理の本体はiscsi_read_pdu。

static int
iscsi_read_pdu(struct spdk_iscsi_conn *conn)
{
    enum iscsi_pdu_recv_state prev_state;
    struct spdk_iscsi_pdu *pdu;
    uint32_t crc32c;
    int ahs_len;
    int rc;

    do {
        prev_state = conn->pdu_recv_state;
        pdu = conn->pdu_in_progress;

        switch (conn->pdu_recv_state) {
        case ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY:
            assert(conn->pdu_in_progress == NULL);

            conn->pdu_in_progress = iscsi_get_pdu(conn);
            if (conn->pdu_in_progress == NULL) {
                return SPDK_ISCSI_CONNECTION_FATAL;
            }
            conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR;
            break;
        case ISCSI_PDU_RECV_STATE_AWAIT_PDU_HDR:
            if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
                rc = iscsi_conn_read_data(conn,
                              ISCSI_BHS_LEN - pdu->bhs_valid_bytes,
                              (uint8_t *)&pdu->bhs + pdu->bhs_valid_bytes);
                if (rc < 0) {
                    conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                    break;
                }
                pdu->bhs_valid_bytes += rc;
                if (pdu->bhs_valid_bytes < ISCSI_BHS_LEN) {
                    return 0;
                }
            }

            /* conn->is_logged_out must be checked after completing to process
             * logout request, i.e., before processing PDU header in this state
             * machine, otherwise logout response may not be sent to initiator
             * and initiator may get logout timeout.
             */
            if (spdk_unlikely(conn->is_logged_out)) {
                SPDK_DEBUGLOG(iscsi, "pdu received after logout\n");
                conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                break;
            }

            pdu->data_segment_len = ISCSI_ALIGN(DGET24(pdu->bhs.data_segment_len));
            pdu->data_buf_len = pdu->data_segment_len;

            /* AHS */
            ahs_len = pdu->bhs.total_ahs_len * 4;
            if (ahs_len > ISCSI_AHS_LEN) {
                SPDK_DEBUGLOG(iscsi, "pdu ahs length %d is invalid\n", ahs_len);
                conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                break;
            }

            if (pdu->ahs_valid_bytes < ahs_len) {
                rc = iscsi_conn_read_data(conn,
                              ahs_len - pdu->ahs_valid_bytes,
                              pdu->ahs + pdu->ahs_valid_bytes);
                if (rc < 0) {
                    conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                    break;
                }

                pdu->ahs_valid_bytes += rc;
                if (pdu->ahs_valid_bytes < ahs_len) {
                    return 0;
                }
            }

            /* Header Digest */
            if (conn->header_digest &&
                pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
                rc = iscsi_conn_read_data(conn,
                              ISCSI_DIGEST_LEN - pdu->hdigest_valid_bytes,
                              pdu->header_digest + pdu->hdigest_valid_bytes);
                if (rc < 0) {
                    conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                    break;
                }

                pdu->hdigest_valid_bytes += rc;
                if (pdu->hdigest_valid_bytes < ISCSI_DIGEST_LEN) {
                    return 0;
                }
            }

            if (conn->header_digest) {
                crc32c = iscsi_pdu_calc_header_digest(pdu);
                rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c);
                if (rc == 0) {
                    SPDK_ERRLOG("header digest error (%s)\n", conn->initiator_name);
                    conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                    break;
                }
            }

            rc = iscsi_pdu_hdr_handle(conn, pdu);
            if (rc < 0) {
                SPDK_ERRLOG("Critical error is detected. Close the connection\n");
                conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                break;
            }

            conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD;
            break;
        case ISCSI_PDU_RECV_STATE_AWAIT_PDU_PAYLOAD:
            if (pdu->data_segment_len != 0) {
                rc = iscsi_pdu_payload_read(conn, pdu);
                if (rc > 0) {
                    return 0;
                } else if (rc < 0) {
                    conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
                    break;
                }
            }

            /* All data for this PDU has now been read from the socket. */
            spdk_trace_record(TRACE_ISCSI_READ_PDU, conn->id, pdu->data_valid_bytes,
                      (uintptr_t)pdu, pdu->bhs.opcode);

            if (!pdu->is_rejected) {
                rc = iscsi_pdu_payload_handle(conn, pdu);
            } else {
                rc = 0;
            }
            if (rc == 0) {
                spdk_trace_record(TRACE_ISCSI_TASK_EXECUTED, 0, 0, (uintptr_t)pdu);
                iscsi_put_pdu(pdu);
                conn->pdu_in_progress = NULL;
                conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_AWAIT_PDU_READY;
                return 1;
            } else {
                conn->pdu_recv_state = ISCSI_PDU_RECV_STATE_ERROR;
            }
            break;
        case ISCSI_PDU_RECV_STATE_ERROR:
            return SPDK_ISCSI_CONNECTION_FATAL;
        default:
            assert(false);
            SPDK_ERRLOG("code should not come here\n");
            break;
        }
    } while (prev_state != conn->pdu_recv_state);

    return 0;
}

static int
iscsi_pdu_payload_handle(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
{
    int opcode;
    int rc = 0;

    opcode = pdu->bhs.opcode;

    SPDK_DEBUGLOG(iscsi, "opcode %x\n", opcode);

    switch (opcode) {
    case ISCSI_OP_LOGIN:
        rc = iscsi_pdu_payload_op_login(conn, pdu);
        break;
    case ISCSI_OP_NOPOUT:
        rc = iscsi_pdu_payload_op_nopout(conn, pdu);
        break;
    case ISCSI_OP_SCSI:
        rc = iscsi_pdu_payload_op_scsi(conn, pdu);
        break;
    case ISCSI_OP_TASK:
        break;
    case ISCSI_OP_TEXT:
        rc = iscsi_pdu_payload_op_text(conn, pdu);
        break;
    case ISCSI_OP_LOGOUT:
        break;
    case ISCSI_OP_SCSI_DATAOUT:
        rc = iscsi_pdu_payload_op_data(conn, pdu);
        break;
    case ISCSI_OP_SNACK:
        break;
    default:
        SPDK_ERRLOG("unsupported opcode %x\n", opcode);
        return iscsi_reject(conn, pdu, ISCSI_REASON_PROTOCOL_ERROR);
    }

    if (rc < 0) {
        SPDK_ERRLOG("processing PDU payload (opcode=%x) failed on %s(%s)\n",
                opcode,
                conn->target_port != NULL ? spdk_scsi_port_get_name(conn->target_port) : "NULL",
                conn->initiator_port != NULL ? spdk_scsi_port_get_name(conn->initiator_port) : "NULL");
    }

    return rc;
}
SendTargets Operation

   To reduce the amount of configuration required on an initiator, iSCSI
   provides the SendTargets text request.  The initiator uses the
   SendTargets request to get a list of targets to which it may have
   access, as well as the list of addresses (IP address and TCP port) on
   which these targets may be accessed.

iSCSIではイニシエータはSendTargetsテキスト要求でtargetのリストを取得する。

static int
iscsi_pdu_payload_op_text(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu)
{
    struct iscsi_param *params = NULL;
    struct spdk_iscsi_pdu *rsp_pdu;
    uint8_t *data;
    uint64_t lun;
    uint32_t task_tag;
    const char *val;
    int F_bit, C_bit;
    int data_len;
    int alloc_len;
    int rc;
    struct iscsi_bhs_text_req *reqh;
    struct iscsi_bhs_text_resp *rsph;

    data_len = 0;
    alloc_len = conn->MaxRecvDataSegmentLength;

    reqh = (struct iscsi_bhs_text_req *)&pdu->bhs;

    F_bit = !!(reqh->flags & ISCSI_FLAG_FINAL);
    C_bit = !!(reqh->flags & ISCSI_TEXT_CONTINUE);
    lun = from_be64(&reqh->lun);
    task_tag = from_be32(&reqh->itt);

    /* store incoming parameters */
    rc = iscsi_parse_params(&params, pdu->data, pdu->data_segment_len,
                C_bit, &conn->partial_text_parameter);
    if (rc < 0) {
        SPDK_ERRLOG("iscsi_parse_params() failed\n");
        iscsi_param_free(params);
        return -1;
    }

    if (pdu->data_segment_len == 0 && params == NULL) {
        params = conn->params_text;
        conn->params_text = NULL;
    }

    data = calloc(1, alloc_len);
    if (!data) {
        SPDK_ERRLOG("calloc() failed for data segment\n");
        iscsi_param_free(params);
        return -ENOMEM;
    }

    /* negotiate parameters */
    data_len = iscsi_negotiate_params(conn, &params,
                      data, alloc_len, data_len);
    if (data_len < 0) {
        SPDK_ERRLOG("iscsi_negotiate_params() failed\n");
        iscsi_param_free(params);
        free(data);
        return -1;
    }

  ★sendtargetsのケース
    /* sendtargets is special case */
    val = iscsi_param_get_val(params, "SendTargets");
    if (val != NULL) {
        if (iscsi_param_eq_val(conn->sess->params,
                       "SessionType", "Discovery")) {
            if (strcasecmp(val, "") == 0) {
                val = "ALL"; ★特にTarget名の指定がなければ"ALL"
            }

      ★iscsi_send_tgtsの本体。
            data_len = iscsi_send_tgts(conn,
                           conn->initiator_name,
                           val, data, alloc_len,
                           data_len);
        } else {
            if (strcasecmp(val, "") == 0) {
                val = conn->target->name;
            }

            if (strcasecmp(val, "ALL") == 0) {
                /* not in discovery session */
                data_len = iscsi_append_text("SendTargets", "Reject",
                                 data, alloc_len, data_len);
            } else {
                data_len = iscsi_send_tgts(conn,
                               conn->initiator_name,
                               val, data, alloc_len,
                               data_len);
            }
        }

        if (conn->send_tgt_completed_size != 0) {
            F_bit = 0;
            C_bit = 1;
        }
    } else {
        if (iscsi_param_eq_val(conn->sess->params, "SessionType", "Discovery")) {
            iscsi_param_free(params);
            free(data);
            return SPDK_ISCSI_CONNECTION_FATAL;
        }
    }

    if (spdk_likely(conn->send_tgt_completed_size == 0)) {
        iscsi_param_free(params);
    } else {
        conn->params_text = params;
    }
    SPDK_LOGDUMP(iscsi, "Negotiated Params", data, data_len);

    /* response PDU */
    rsp_pdu = iscsi_get_pdu(conn);
    if (rsp_pdu == NULL) {
        free(data);
        return SPDK_ISCSI_CONNECTION_FATAL;
    }
    rsph = (struct iscsi_bhs_text_resp *)&rsp_pdu->bhs;

    rsp_pdu->data = data;
    rsph->opcode = ISCSI_OP_TEXT_RSP;

    if (F_bit) {
        rsph->flags |= ISCSI_FLAG_FINAL;
    }

    if (C_bit) {
        rsph->flags |= ISCSI_TEXT_CONTINUE;
    }

    DSET24(rsph->data_segment_len, data_len);
    to_be64(&rsph->lun, lun);
    to_be32(&rsph->itt, task_tag);

    if (F_bit) {
        rsph->ttt = 0xffffffffU;
        conn->sess->current_text_itt = 0xffffffffU;
    } else {
        to_be32(&rsph->ttt, 1 + conn->id);
    }

    to_be32(&rsph->stat_sn, conn->StatSN);
    conn->StatSN++;

    if (reqh->immediate == 0) {
        conn->sess->MaxCmdSN++;
    }

    to_be32(&rsph->exp_cmd_sn, conn->sess->ExpCmdSN);
    to_be32(&rsph->max_cmd_sn, conn->sess->MaxCmdSN);

    iscsi_conn_write_pdu(conn, rsp_pdu, iscsi_conn_text_pdu_complete, conn);
    return 0;
}

int
iscsi_send_tgts(struct spdk_iscsi_conn *conn, const char *iiqn,
        const char *tiqn, uint8_t *data, int alloc_len, int data_len)
{
    struct spdk_iscsi_tgt_node *target;
    int total;
    int len;
    int rc;
    int previous_completed_size = 0;
    bool no_buf_space = false;
    char tmp_buf[MAX_TMP_NAME_BUF];

    if (conn == NULL) {
        return 0;
    }
    previous_completed_size = conn->send_tgt_completed_size;

    total = data_len;
    if (alloc_len < 1) {
        return 0;
    }
    if (total >= alloc_len) {
        total = alloc_len;
        data[total - 1] = '\0';
        return total;
    }

    pthread_mutex_lock(&g_iscsi.mutex);


  ★targetの一覧を調べる。
    TAILQ_FOREACH(target, &g_iscsi.target_head, tailq) {
        if (strcasecmp(tiqn, "ALL") != 0
            && strcasecmp(tiqn, target->name) != 0) {
            continue;
        }

     ★targetのinitiatorが所属するinitiatorグループがいるか調べる。
        rc = iscsi_tgt_node_allow_iscsi_name(target, iiqn);
        if (rc == 0) {
            continue;
        }

        memset(tmp_buf, 0, sizeof(tmp_buf));
        /* Calculate the whole string size */
        len = snprintf(NULL, 0, "TargetName=%s", target->name);
        assert(len < MAX_TMPBUF);

        /* String contents are not copied */
        if (previous_completed_size < len) {
            /* Copy the string into the temporary buffer */
            snprintf(tmp_buf, len + 1, "TargetName=%s", target->name);
        }

        no_buf_space = iscsi_copy_str(data, &total, alloc_len, &previous_completed_size,
                          len + 1, tmp_buf);
        if (no_buf_space) {
            break;
        }

        total = iscsi_send_tgt_portals(conn, target, data, alloc_len, total,
                           &previous_completed_size, &no_buf_space);
        if (no_buf_space) {
            break;
        }
    }
    pthread_mutex_unlock(&g_iscsi.mutex);

    /* Only set it when it is not successfully completed */
    if (no_buf_space) {
        conn->send_tgt_completed_size += total;
    } else {
        conn->send_tgt_completed_size = 0;
    }

    return total;
}

まず、iscsi_tgt_node_allow_iscsi_nameで、initiatorがinitiator groupに登録されたtargetかどうかを調べる。

static bool
iscsi_tgt_node_allow_iscsi_name(struct spdk_iscsi_tgt_node *target, const char *iqn)
{
    struct spdk_iscsi_pg_map *pg_map;
    struct spdk_iscsi_ig_map *ig_map;
    int rc;
    bool result = false;

    if (target == NULL || iqn == NULL) {
        return false;
    }

    TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
        TAILQ_FOREACH(ig_map, &pg_map->ig_map_head, tailq) {
            rc = iscsi_init_grp_allow_iscsi_name(ig_map->ig, iqn, &result);
            if (rc == 0) {
                return result;
            }
        }
    }

    return false;
}

static int
iscsi_send_tgt_portals(struct spdk_iscsi_conn *conn,
               struct spdk_iscsi_tgt_node *target,
               uint8_t *data, int alloc_len, int total,
               int *previous_completed_len, bool *no_buf_space)
{
    char buf[MAX_TARGET_ADDR + 2];
    struct spdk_iscsi_portal_grp *pg;
    struct spdk_iscsi_pg_map *pg_map;
    struct spdk_iscsi_portal *p;
    char *host;
    char tmp_buf[MAX_TMP_ADDR_BUF];
    int len;

    TAILQ_FOREACH(pg_map, &target->pg_map_head, tailq) {
        pg = pg_map->pg;

        if (pg->is_private) {
            /* Skip the private portal group. Portals in the private portal group
             * will be returned only by temporary login redirection responses.
             */
            continue;
        }

        TAILQ_FOREACH(p, &pg->head, per_pg_tailq) {
            host = p->host;
            /* wildcard? */
            if (strcasecmp(host, "[::]") == 0 || strcasecmp(host, "0.0.0.0") == 0) {
                if (spdk_sock_is_ipv6(conn->sock)) {
                    snprintf(buf, sizeof buf, "[%s]", conn->target_addr);
                    host = buf;
                } else if (spdk_sock_is_ipv4(conn->sock)) {
                    snprintf(buf, sizeof buf, "%s", conn->target_addr);
                    host = buf;
                } else {
                    /* skip portal for the family */
                    continue;
                }
            }

            ★interfaceのチェックが必要。
            SPDK_DEBUGLOG(iscsi, "TargetAddress=%s:%s,%d\n",
                      host, p->port, pg->tag);

            memset(tmp_buf, 0, sizeof(tmp_buf));
            /* Calculate the whole string size */
            len = snprintf(NULL, 0, "TargetAddress=%s:%s,%d", host, p->port, pg->tag);
            assert(len < MAX_TMPBUF);

            /* string contents are not fully copied */
      ★ここでテキスト要求を返している。特にインタフェースなどは気にしない。
            if (*previous_completed_len < len) {
                /* Copy the string into the temporary buffer */
                snprintf(tmp_buf, len + 1, "TargetAddress=%s:%s,%d", host, p->port, pg->tag);
            }

            *no_buf_space = iscsi_copy_str(data, &total, alloc_len, previous_completed_len,
                               len + 1, tmp_buf);
            if (*no_buf_space) {
                break;
            }
        }
    }

    return total;
}

connはだれが作っている? iscsi_conn_sock_cbの中からhandlerが呼ばれている。もともとはiscsi_conn_startから。

static void
iscsi_conn_start(void *ctx)
{
    struct spdk_iscsi_conn *conn = ctx;

    iscsi_poll_group_add_conn(conn->pg, conn);

    conn->login_timer = SPDK_POLLER_REGISTER(login_timeout, conn, ISCSI_LOGIN_TIMEOUT * 1000000);
}

static void
iscsi_poll_group_add_conn(struct spdk_iscsi_poll_group *pg, struct spdk_iscsi_conn *conn)
{
    int rc;

    rc = spdk_sock_group_add_sock(pg->sock_group, conn->sock, iscsi_conn_sock_cb, conn);
    if (rc < 0) {
        SPDK_ERRLOG("Failed to add sock=%p of conn=%p\n", conn->sock, conn);
        return;
    }

    conn->is_stopped = false;
    STAILQ_INSERT_TAIL(&pg->connections, conn, pg_link);
}

conn_startはiscsi_portal_acceptから始まる。listenしているソケットからconn_startする。

そのごinitiaterからPDUが来たら、応答を返す。

→connにportalの中身をいれておけば、その後のPDU処理にも使える。

static int
iscsi_portal_accept(void *arg)
{
    struct spdk_iscsi_portal    *portal = arg;
    struct spdk_sock        *sock;
    int             rc;
    int             count = 0;

    if (portal->sock == NULL) {
        return -1;
    }

    while (1) {
        sock = spdk_sock_accept(portal->sock);
        if (sock != NULL) {
            rc = iscsi_conn_construct(portal, sock);
            if (rc < 0) {
                spdk_sock_close(&sock);
                SPDK_ERRLOG("spdk_iscsi_connection_construct() failed\n");
                break;
            }
            count++;
        } else {
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                SPDK_ERRLOG("accept error(%d): %s\n", errno, spdk_strerror(errno));
            }
            break;
        }
    }

    return count;
}

int
iscsi_conn_construct(struct spdk_iscsi_portal *portal,
             struct spdk_sock *sock)
{
    struct spdk_iscsi_poll_group *pg;
    struct spdk_iscsi_conn *conn;
    int i, rc;

    conn = allocate_conn();
    if (conn == NULL) {
        SPDK_ERRLOG("Could not allocate connection.\n");
        return -1;
    }

    pthread_mutex_lock(&g_iscsi.mutex);
    conn->timeout = g_iscsi.timeout * spdk_get_ticks_hz(); /* seconds to TSC */
    conn->nopininterval = g_iscsi.nopininterval;
    conn->nopininterval *= spdk_get_ticks_hz(); /* seconds to TSC */
/iscsi_conn_start
        SPDK_ERRLOG("spdk_sock_set_recvlowat() failed\n");
        goto error_return;
    }

    /* set default params */
    rc = iscsi_conn_params_init(&conn->params);
    if (rc < 0) {
        SPDK_ERRLOG("iscsi_conn_params_init() failed\n");
        goto error_return;
    }
    conn->logout_request_timer = NULL;
    conn->logout_timer = NULL;
    conn->shutdown_timer = NULL;
    SPDK_DEBUGLOG(iscsi, "Launching connection on acceptor thread\n");
    conn->pending_task_cnt = 0;

    /* Get the first poll group. */
    pg = TAILQ_FIRST(&g_iscsi.poll_group_head);
    if (pg == NULL) {
        SPDK_ERRLOG("There is no poll group.\n");
        assert(false);
        goto error_return;
    }

  ★ここでconnをスタートする。
    conn->pg = pg;
    spdk_thread_send_msg(spdk_io_channel_get_thread(spdk_io_channel_from_ctx(pg)),
                 iscsi_conn_start, conn);
    return 0;

error_return:
    iscsi_param_free(conn->params);
    free_conn(conn);
    return -1;
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です