open-iscsi源碼分析 -- 會話建立

前面可以看出,處理控制中心還是iscsid守護進程,其他進程都是向它發出服務請求,然後它做出響應,而它完成一些操作又是向底層驅動發出指令。在前面的源碼中可以發現,重要是一個mgmt_ipc_functions的函數指針數組是一個操作函數集合,其中的函數分別處理不同的服務,服務id就是它在函數指針數組中的索引。
/*不同任務id對應的處理函數地址*/
static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
[MGMT_IPC_SESSION_LOGIN] = mgmt_ipc_session_login, /*和iscsi target建立連接*/
[MGMT_IPC_SESSION_LOGOUT] = mgmt_ipc_session_logout, /*與target斷開會話*/
[MGMT_IPC_SESSION_SYNC]  = mgmt_ipc_session_sync, /*同步會話*/
[MGMT_IPC_SESSION_STATS] = mgmt_ipc_session_getstats, /*查詢會話狀態*/
[MGMT_IPC_SEND_TARGETS]  = mgmt_ipc_send_targets, /*發送targets*/
[MGMT_IPC_SESSION_INFO]  = mgmt_ipc_session_info, /*查詢會話信息*/
[MGMT_IPC_CONN_ADD]  = mgmt_ipc_conn_add, /*增加連接*/
[MGMT_IPC_CONN_REMOVE]  = mgmt_ipc_conn_remove, /*刪除連接*/
[MGMT_IPC_CONFIG_INAME]  = mgmt_ipc_cfg_initiatorname, /*iscsi 配置,獲取initiatorname*/
[MGMT_IPC_CONFIG_IALIAS] = mgmt_ipc_cfg_initiatoralias, /*獲取initiator 別名*/
[MGMT_IPC_CONFIG_FILE]  = mgmt_ipc_cfg_filename, /*獲取配置文件路徑*/
[MGMT_IPC_IMMEDIATE_STOP] = mgmt_ipc_immediate_stop,/*停止*/
[MGMT_IPC_NOTIFY_ADD_NODE] = mgmt_ipc_notify_add_node, /*增加一個node*/
[MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node, /*刪除一個node*/
[MGMT_IPC_NOTIFY_ADD_PORTAL] = mgmt_ipc_notify_add_portal, /*增加端口*/
[MGMT_IPC_NOTIFY_DEL_PORTAL] = mgmt_ipc_notify_del_portal, /*刪除端口*/
};
 
現在分析第一個操作函數mgmt_ipc_session_login。
/*建立會話*/
static int 
mgmt_ipc_session_login(queue_task_t *qtask)
{
   /*專門傳入task結構中會話請求的數據結構*/
 return session_login_task(&qtask->req.u.session.rec, qtask);
}
/*login建立initiator和target的會話*/
int
session_login_task(node_rec_t *rec, queue_task_t *qtask)
{
 iscsi_session_t *session;
 iscsi_conn_t *conn;
 struct iscsi_transport *t;
 int rc;
 /*判斷這個連接是否已經存在*/
 if (session_is_running(rec)) {
  if (rec->session.multiple)
   log_debug(2, "Adding a copy of an existing session");
  else
   return ISCSI_ERR_SESS_EXISTS;
 }
 
先貼出這些,接着進入session_is_running函數進行分析:
/*
 * a session could be running in the kernel but not in iscsid
 * due to a resync or becuase some other app started the session
 */
 /*判斷是否已經存在這個會話連接*/
static int session_is_running(node_rec_t *rec)
{
 int nr_found = 0;
 /*通過會話請求結構在系統中查找*/
 if (session_find_by_rec(rec))
  return 1;
   /*在sys目錄下查找*/
 if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session))
  return 1;
 return 0;
}
/*根據會話請求結構返回系統中已存在的會話結構*/
static iscsi_session_t* session_find_by_rec(node_rec_t *rec)
{
 struct iscsi_transport *t;
 iscsi_session_t *session;
 /*遍歷系統中的每個transport結構*/
 list_for_each_entry(t, &transports, list) {
  list_for_each_entry(session, &t->sessions, list) { /*transport鏈中的每個是session鏈的頭結點,還要遍歷*/
   if (__iscsi_match_session(rec, session->nrec.name, //targetname
      session->nrec.conn[0].address, //target ip
      session->nrec.conn[0].port, //target port
      &session->nrec.iface, //網絡接口
      MATCH_ANY_SID))
    return session;
  }
 }
 return NULL;
}
 
//對會話結構進行比較匹配
int __iscsi_match_session(node_rec_t *rec, char *targetname,
     char *address, int port, struct iface_rec *iface,
     unsigned sid)
{
 if (!rec) {
  log_debug(6, "no rec info to match\n");
  return 1;
 }
 log_debug(6, "match session [%s,%s,%d][%s %s,%s,%s]:%u",
    rec->name, rec->conn[0].address, rec->conn[0].port,
    rec->iface.name, rec->iface.transport_name,
    rec->iface.hwaddress, rec->iface.ipaddress,
    rec->session.sid);
 if (iface)
  log_debug(6, "to [%s,%s,%d][%s %s,%s,%s]:%u",
     targetname, address, port, iface->name,
     iface->transport_name, iface->hwaddress,
     iface->ipaddress, sid);
   /*若指定會話sid匹配,又匹配不上,則直接返回*/
 if (rec->session.sid && sid && rec->session.sid != sid)
  return 0;
 /*比較target name*/
 if (strlen(rec->name) && strcmp(rec->name, targetname))
  return 0;
 /*比較ip地址*/
 if (!iscsi_addr_match(rec->conn[0].address, address))
  return 0;
    /*端口比較*/
 if (rec->conn[0].port != -1 && port != rec->conn[0].port)
  return 0;
 /*iface網絡接口比較*/
 if (!iface_match(&rec->iface, iface))
  return 0;
 return 1;
}
/**
 * iscsi_addr_match - check if the addrs are to the same ip
 * @address1: pattern
 * @address2: address to check
 *
 * If address1 is blank then it matches any string passed in.
 */
static int iscsi_addr_match(char *address1, char *address2)
{
 struct addrinfo hints1, hints2, *res1, *res2;
 int rc;
 if (!strlen(address1)) /*address1爲空 則匹配中任何ip*/
  return 1;
 if (!strcmp(address1, address2))/*直接ip字符串匹配*/
  return 1;
 memset(&hints1, 0, sizeof(struct addrinfo));
 hints1.ai_family = AF_UNSPEC;
 hints1.ai_socktype = SOCK_STREAM;
 memset(&hints2, 0, sizeof(struct addrinfo));
 hints2.ai_family = AF_UNSPEC;
 hints2.ai_socktype = SOCK_STREAM;
 /*
  * didn't match so we have to resolve to see if one is a dnsname
  * that matches a ip address.
  */
  /*若上面沒有匹配中,則要查看是否傳入的是域名,需要先解析*/
 rc = getaddrinfo(address1, NULL, &hints1, &res1);
 if (rc) {
  log_debug(1, "Match error. Could not resolve %s: %s", address1,
     gai_strerror(rc));
  return 0;
 }
 rc = getaddrinfo(address2, NULL, &hints2, &res2);
 if (rc) {
  log_debug(1, "Match error. Could not resolve %s: %s", address2,
     gai_strerror(rc));
  rc = 0;
  goto free_res1;
 }
 /*將返回的ip進行比較*/
 if ((res1->ai_addrlen != res2->ai_addrlen) ||
     memcmp(res1->ai_addr, res2->ai_addr, res2->ai_addrlen))
  rc = 0;
 else
  rc = 1;
 freeaddrinfo(res2);
free_res1:
 freeaddrinfo(res1);
 return rc;
}
 
/*iface網絡接口比較 */
int iface_match(struct iface_rec *pattern, struct iface_rec *iface)
{
 if (!pattern || !iface) //任一爲空,則匹配中
  return 1;
 if (!strlen(pattern->name))
  return 1;
 if (!strcmp(pattern->name, iface->name)) {
  if (!strcmp(pattern->name, DEFAULT_IFACENAME))
   return 1;
  /*
   * For default we allow the same name, but different
   * transports.
   */
  if (!strlen(pattern->transport_name))
   return 1;
  if (!strcmp(pattern->transport_name, iface->transport_name))
   return 1;
  /* fall through */
 }
 return 0;
}
另一個查找路徑:
/*在sys目錄下查找*/
int iscsi_sysfs_for_each_session(void *data, int *nr_found,
     iscsi_sysfs_session_op_fn *fn)
{
 struct dirent **namelist;
 int rc = 0, n, i;
 struct session_info *info;
 info = calloc(1, sizeof(*info));
 if (!info)
  return ISCSI_ERR_NOMEM;
    //掃描目錄下的所有目錄名和文件,但不會進入子目錄掃面
 n = scandir(ISCSI_SESSION_DIR, &namelist, trans_filter,
      alphasort);
 if (n <= 0)
  goto free_info;
 for (i = 0; i < n; i++) {
  rc = iscsi_sysfs_get_sessioninfo_by_id(info, //根據會話名返回會話的信息
             namelist[i]->d_name);
  if (rc) {
   log_error("could not find session info for %s",
       namelist[i]->d_name);
   /* raced. session was shutdown while looping */
   rc = 0;
   continue;
  }
  rc = fn(data, info);
  if (rc > 0)
   break;
  else if (rc == 0)
   (*nr_found)++;
  else
   /* if less than zero it means it was not a match */
   rc = 0;
 }
 for (i = 0; i < n; i++)
  free(namelist[i]);
 free(namelist);
free_info:
 free(info);
 return rc;
}
/*根據會話id,返回會話信息*/
int iscsi_sysfs_get_sessioninfo_by_id(struct session_info *info, char *session)
{
 char id[NAME_SIZE];
 int ret, pers_failed = 0;
 uint32_t host_no;
 if (sscanf(session, "session%d", &info->sid) != 1) { //獲取會話id
  log_error("invalid session '%s'", session);
  return ISCSI_ERR_INVAL;
 }
 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS, "targetname",
       info->targetname, sizeof(info->targetname));
 if (ret) {
  log_error("could not read session targetname: %d", ret);
  return ISCSI_ERR_SYSFS_LOOKUP;
 }
 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS, "username",
    (info->chap).username,
    sizeof((info->chap).username));
 if (ret)
  log_debug(5, "could not read username: %d", ret);
 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS, "password",
    (info->chap).password,
    sizeof((info->chap).password));
 if (ret)
  log_debug(5, "could not read password: %d", ret);
 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS, "username_in",
    (info->chap).username_in,
    sizeof((info->chap).username_in));
 if (ret)
  log_debug(5, "could not read username in: %d", ret);
 ret = sysfs_get_str(session, ISCSI_SESSION_SUBSYS, "password_in",
    (info->chap).password_in,
    sizeof((info->chap).password_in));
 if (ret)
  log_debug(5, "could not read password in: %d", ret);
 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "recovery_tmo",
    &((info->tmo).recovery_tmo));
 if (ret)
  (info->tmo).recovery_tmo = -1;
 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "lu_reset_tmo",
    &((info->tmo).lu_reset_tmo));
 if (ret)
  (info->tmo).lu_reset_tmo = -1;
 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "tgt_reset_tmo",
    &((info->tmo).tgt_reset_tmo));
 if (ret)
  (info->tmo).lu_reset_tmo = -1;
 sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "abort_tmo",
    &((info->tmo).abort_tmo));
 if (ret)
  (info->tmo).abort_tmo = -1;
 ret = sysfs_get_int(session, ISCSI_SESSION_SUBSYS, "tpgt",
       &info->tpgt);
 if (ret) {
  log_error("could not read session tpgt: %d", ret);
  return ISCSI_ERR_SYSFS_LOOKUP;
 }
 snprintf(id, sizeof(id), ISCSI_CONN_ID, info->sid);
 /* some HW drivers do not export addr and port */
 memset(info->persistent_address, 0, NI_MAXHOST);
 ret = sysfs_get_str(id, ISCSI_CONN_SUBSYS, "persistent_address",
       info->persistent_address,
       sizeof(info->persistent_address));
 if (ret) {
  pers_failed = 1;
  /* older qlogic does not support this */
  log_debug(5, "could not read pers conn addr: %d", ret);
 }
 memset(info->address, 0, NI_MAXHOST);
 ret = sysfs_get_str(id, ISCSI_CONN_SUBSYS, "address",
       info->address, sizeof(info->address));
 if (ret) {
  log_debug(5, "could not read curr addr: %d", ret);
  /* iser did not export this */
  if (!pers_failed)
   strcpy(info->address, info->persistent_address);
 } else if (pers_failed)
  /*
   * for qla if we could not get the persistent addr
   * we will use the current for both addrs
   */
  strcpy(info->persistent_address, info->address);
 pers_failed = 0;
 info->persistent_port = -1;
 ret = sysfs_get_int(id, ISCSI_CONN_SUBSYS, "persistent_port",
       &info->persistent_port);
 if (ret) {
  pers_failed = 1;
  log_debug(5, "Could not read pers conn port %d", ret);
 }
 info->port = -1;
 ret = sysfs_get_int(id, ISCSI_CONN_SUBSYS, "port", &info->port);
 if (ret) {
  /* iser did not export this */
  if (!pers_failed)
   info->port = info->persistent_port;
  log_debug(5, "Could not read curr conn port %d", ret);
 } else if (pers_failed)
  /*
   * for qla if we could not get the persistent addr
   * we will use the current for both addrs
   */
  info->persistent_port = info->port;
 ret = 0;
 host_no = iscsi_sysfs_get_host_no_from_sid(info->sid, &ret);
 if (ret) {
  log_error("could not get host_no for session%d: %s.",
     info->sid, iscsi_err_to_str(ret));
  return ret;
 }
 iscsi_sysfs_read_iface(&info->iface, host_no, session, NULL);
 log_debug(7, "found targetname %s address %s pers address %s port %d "
   "pers port %d driver %s iface name %s ipaddress %s "
   "netdev %s hwaddress %s iname %s",
    info->targetname, info->address ? info->address : "NA",
    info->persistent_address ? info->persistent_address : "NA",
    info->port, info->persistent_port, info->iface.transport_name,
    info->iface.name, info->iface.ipaddress,
    info->iface.netdev, info->iface.hwaddress,
    info->iface.iname);
 return 0;
}

判斷會話是否存在後。若存在則直接返回,否則接着進行創建會話等:
open-iscsi支持這幾種傳輸模式:
static struct iscsi_transport_template *iscsi_transport_templates[] = {
&iscsi_tcp,
&iscsi_iser,
&cxgb3i,
&cxgb4i,
&bnx2i,
&qla4xxx,
&be2iscsi,
NULL
};
對應的是一個結構體,這個結構體中是一些操作函數:
truct iscsi_transport_template iscsi_tcp = {
.name = "tcp",
.ep_connect = iscsi_io_tcp_connect,
.ep_poll = iscsi_io_tcp_poll,
.ep_disconnect = iscsi_io_tcp_disconnect,
};

struct iscsi_transport_template iscsi_iser = {
.name = "iser",
.rdma = 1,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
.create_conn = iser_create_conn,
};

struct iscsi_transport_template cxgb3i = {
.name = "cxgb3i",
.set_host_ip = 1,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
.create_conn = cxgbi_create_conn,
};

struct iscsi_transport_template cxgb4i = {
.name = "cxgb4i",
.set_host_ip = 1,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
.create_conn = cxgbi_create_conn,
};

struct iscsi_transport_template bnx2i = {
.name = "bnx2i",
.set_host_ip = 1,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
};

struct iscsi_transport_template be2iscsi = {
.name = "be2iscsi",
.create_conn = be2iscsi_create_conn,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
};

struct iscsi_transport_template qla4xxx = {
.name = "qla4xxx",
.set_host_ip = 0,
.ep_connect = ktransport_ep_connect,
.ep_poll = ktransport_ep_poll,
.ep_disconnect = ktransport_ep_disconnect,
};

在查找會話是否存在後,搜索對應的transport模式:
t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name);
在創建會話,建立連接的過程中需要用到傳輸模式對應結構中的連接函數。
/*根據傳輸模式的名稱,返回這個結構體*/
struct iscsi_transport *iscsi_sysfs_get_transport_by_name(char *transport_name)
{
struct iscsi_transport *t;
int retry = 0;

retry:
/* sync up kernel and userspace */
/*將內核和用戶空間支持的傳輸模式進行同步*/
read_transports();

/* check if the transport is loaded and matches */
list_for_each_entry(t, &transports, list) {
if (t->handle && !strncmp(t->name, transport_name,
  ISCSI_TRANSPORT_NAME_MAXLEN))
return t;
}

if (retry < 1) {
retry++;
if (!transport_load_kmod(transport_name)) //判斷對應的內核模塊是否加載
goto retry;
}

return NULL;
}

/*將用戶空間支持的傳輸模式和內核同步*/
static int read_transports(void)
{
struct dirent **namelist;
int i, n, found;
struct iscsi_transport *t;

log_debug(7, "in %s", __FUNCTION__);

//對ISCSI_TRANSPORT_DIR目錄進行掃描
//這個目錄下的軟鏈接名稱就是系統支持的傳輸模式
n = scandir(ISCSI_TRANSPORT_DIR, &namelist, trans_filter,
    alphasort);
if (n < 0) {
log_error("Could not scan %s.", ISCSI_TRANSPORT_DIR);
return n;
}

for (i = 0; i < n; i++) {
found = 0;

list_for_each_entry(t, &transports, list) { //在系統的全局變量鏈表transports中查找
if (!strcmp(t->name, namelist[i]->d_name)) { //找到了transport名稱
found = 1;
break;
}
}

if (!found) {
/* copy new transport */
t = malloc(sizeof(*t)); //沒有這個名稱,則分配一個transport結構
if (!t)
continue;
log_debug(7, "Adding new transport %s",
  namelist[i]->d_name);

INIT_LIST_HEAD(&t->sessions);
INIT_LIST_HEAD(&t->list);
strlcpy(t->name, namelist[i]->d_name,
ISCSI_TRANSPORT_NAME_MAXLEN);
if (set_transport_template(t)) { //設置對應的模版操作函數
free(t);
return -1;
}
} else
log_debug(7, "Updating transport %s",
  namelist[i]->d_name);

if (sysfs_get_uint64(t->name, ISCSI_TRANSPORT_SUBSYS,
     "handle", &t->handle)) { //在iscsi錩transport目錄下獲取handle值,handle是一個文件名,值在這個文件中
if (list_empty(&t->list))
free(t);
else
log_error("Could not update %s.\n",
  t->name);
continue;
}

if (sysfs_get_uint(t->name, ISCSI_TRANSPORT_SUBSYS,
  "caps", &t->caps)) { //同上,回去caps值
if (list_empty(&t->list))
free(t);
else
log_error("Could not update %s.\n",
  t->name);
continue;
}
/*
 * tmp hack for qla4xx compat
 */
if (!strcmp(t->name, "qla4xxx")) {
t->caps |= CAP_DATA_PATH_OFFLOAD;
}

if (list_empty(&t->list))
list_add_tail(&t->list, &transports); //將新建的tranport結構體加入到全局變量鏈表中
}

for (i = 0; i < n; i++) //釋放scandir分配的結構
free(namelist[i]);
free(namelist);
num_transports = n; //支持的傳輸模式數量

return 0;
}

#ifdef USE_KMOD
int transport_load_kmod(char *transport_name)
{
struct kmod_ctx *ctx;
struct kmod_module *mod;
int rc;

ctx = kmod_new(NULL, NULL);
if (!ctx) {
log_error("Could not load transport module %s. Out of "
  "memory.", transport_name);
return ISCSI_ERR_NOMEM;
}

kmod_load_resources(ctx);

/*
 * dumb dumb dumb - named iscsi_tcp and ib_iser differently from
 * transport name
 */
if (!strcmp(transport_name, "tcp"))
rc = kmod_module_new_from_name(ctx, "iscsi_tcp", &mod);
else if (!strcmp(transport_name, "iser"))
rc = kmod_module_new_from_name(ctx, "ib_iser", &mod);
else
rc = kmod_module_new_from_name(ctx, transport_name, &mod);
if (rc) {
log_error("Failed to load module %s.", transport_name);
rc = ISCSI_ERR_TRANS_NOT_FOUND;
goto unref_mod;
}

rc = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST,
     NULL, NULL, NULL, NULL);
if (rc) {
log_error("Could not insert module %s. Kmod error %d",
  transport_name, rc);
rc = ISCSI_ERR_TRANS_NOT_FOUND;
}
kmod_module_unref(mod);

unref_mod:
kmod_unref(ctx);
return rc;
}

#else

int transport_load_kmod(char *transport_name)
{
char *cmdline[4];
pid_t pid;

cmdline[0] = "/sbin/modprobe";
cmdline[1] = "-qb";
cmdline[3] = NULL;

/*
 * dumb dumb mistake - named iscsi_tcp and ib_iser differently from
 * transport name
 */
if (!strcmp(transport_name, "tcp"))
cmdline[2] = "iscsi_tcp";
else if (!strcmp(transport_name, "iser"))
cmdline[2] = "ib_iser";
else
cmdline[2] = transport_name;

if (iscsi_sysfs_is_transport_loaded(cmdline[2]))
return 0;

pid = fork();
if (pid == 0) {
if (execv("/sbin/modprobe", cmdline) < 0) {
log_error("Failed to load module %s: %s",
   transport_name, strerror(errno));
exit(-errno);
}
exit(0);
} else if (pid < 0) {
log_error("Failed to fork process to load module %s: %s",
  transport_name, strerror(errno));
return ISCSI_ERR_TRANS_NOT_FOUND;
}

if (waitpid(pid, NULL, 0) < 0) {
log_error("Failed to load module %s", transport_name);
return ISCSI_ERR_TRANS_NOT_FOUND;
}

return 0;
}

#endif

/*設置對應傳輸模式的模版操作函數*/
int set_transport_template(struct iscsi_transport *t)
{
struct iscsi_transport_template *tmpl;
int j;

for (j = 0; iscsi_transport_templates[j] != NULL; j++) { //已支持的傳輸模式的模版函數數組
tmpl = iscsi_transport_templates[j];

if (!strcmp(tmpl->name, t->name)) { //比較傳輸模式名稱是否相同
t->template = tmpl; /*一樣則設置模版函數*/
log_debug(3, "Matched transport %s\n", t->name);
return 0;
}
}

log_error("Could not find template for %s. An updated iscsiadm "
  "is probably needed.\n", t->name);
return ENOSYS;
}

//進入最主要的工作,創建會話
session = __session_create(rec, t);

*這裏主要是分配一個session結構,並進行一些設置*/
static iscsi_session_t*
__session_create(node_rec_t *rec, struct iscsi_transport *t)
{
iscsi_session_t *session;
int hostno, rc = 0;

session = calloc(1, sizeof (*session));  //分配session結構
if (session == NULL) {
log_debug(1, "can not allocate memory for session");
return NULL;
}
log_debug(2, "Allocted session %p", session);

INIT_LIST_HEAD(&session->list);
session->t = t; //設置其transport結構
session->reopen_qtask.mgmt_ipc_fd = -1;
session->id = -1;
session->use_ipc = 1;

/* save node record. we might need it for redirection */
memcpy(&session->nrec, rec, sizeof(node_rec_t)); //保存node記錄

session->portal_group_tag = rec->tpgt;
session->type = ISCSI_SESSION_TYPE_NORMAL;
session->r_stage = R_STAGE_NO_CHANGE;
strlcpy(session->target_name, rec->name, TARGET_NAME_MAXLEN);

if (strlen(session->nrec.iface.iname))
session->initiator_name = session->nrec.iface.iname;
else if (dconfig->initiator_name)
session->initiator_name = dconfig->initiator_name;
else {
log_error("No initiator name set. Cannot create session.");
free(session);
return NULL;
}

if (strlen(session->nrec.iface.alias))
session->initiator_alias = session->nrec.iface.alias;
else
session->initiator_alias = dconfig->initiator_alias;

/* session's eh parameters */
session->replacement_timeout = rec->session.timeo.replacement_timeout;
session->fast_abort = rec->session.iscsi.FastAbort;
session->abort_timeout = rec->session.err_timeo.abort_timeout;
session->lu_reset_timeout = rec->session.err_timeo.lu_reset_timeout;
session->tgt_reset_timeout = rec->session.err_timeo.tgt_reset_timeout;
session->host_reset_timeout = rec->session.err_timeo.host_reset_timeout;

/* OUI and uniqifying number */
session->isid[0] = DRIVER_ISID_0;
session->isid[1] = DRIVER_ISID_1;
session->isid[2] = DRIVER_ISID_2;
session->isid[3] = 0;
session->isid[4] = 0;
session->isid[5] = 0;

/* setup authentication variables for the session*/
iscsi_setup_authentication(session, &rec->session.auth);

session->param_mask = ~0ULL;
if (!(t->caps & CAP_MULTI_R2T))
session->param_mask &= ~ISCSI_MAX_R2T;
if (!(t->caps & CAP_HDRDGST))
session->param_mask &= ~ISCSI_HDRDGST_EN;
if (!(t->caps & CAP_DATADGST))
session->param_mask &= ~ISCSI_DATADGST_EN;
if (!(t->caps & CAP_MARKERS)) {
session->param_mask &= ~ISCSI_IFMARKER_EN;
session->param_mask &= ~ISCSI_OFMARKER_EN;
}

hostno = iscsi_sysfs_get_host_no_from_hwinfo(&rec->iface, &rc);
if (!rc) {
/*
 * if the netdev or mac was set, then we are going to want
 * to want to bind the all the conns/eps to a specific host
 * if offload is used.
 */
session->conn[0].bind_ep = 1;
session->hostno = hostno;
}

list_add_tail(&session->list, &t->sessions);//將新建的session結構,加入系統session鏈表中
return session;
}

//與target端建立連接
rc = __session_conn_create(session, 0);
if (rc) {
__session_destroy(session);
return rc;
}
conn = &session->conn[0];
qtask->conn = conn;

//與target端建立連接
static int
__session_conn_create(iscsi_session_t *session, int cid)
{
iscsi_conn_t *conn = &session->conn[cid];
conn_rec_t *conn_rec = &session->nrec.conn[cid];
int err;

if (iscsi_ev_context_alloc(conn)) {
log_error("cannot allocate context_pool for conn cid %d", cid);
return ISCSI_ERR_NOMEM;
}

conn->state = ISCSI_CONN_STATE_FREE;
conn->session = session;
/*
 * TODO: we must export the socket_fd/transport_eph from sysfs
 * so if iscsid is resyncing up we can pick that up and cleanup up
 * the old connection. Right now we leak a connection.
 * We can also probably merge these two fields.
 */
conn->socket_fd = -1;
conn->transport_ep_handle = -1;
/* connection's timeouts */
conn->id = cid;
conn->logout_timeout = conn_rec->timeo.logout_timeout;
if (!conn->logout_timeout) {
log_error("Invalid timeo.logout_timeout. Must be greater "
  "than zero. Using default %d.\n",
  DEF_LOGOUT_TIMEO);
conn->logout_timeout = DEF_LOGOUT_TIMEO;
}

conn->login_timeout = conn_rec->timeo.login_timeout;
if (!conn->login_timeout) {
log_error("Invalid timeo.login_timeout. Must be greater "
  "than zero. Using default %d.\n",
  DEF_LOGIN_TIMEO);
conn->login_timeout = DEF_LOGIN_TIMEO;
}

conn->auth_timeout = conn_rec->timeo.auth_timeout;

/* noop-out setting */
conn->noop_out_interval = conn_rec->timeo.noop_out_interval;
conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout;
if (conn->noop_out_interval && !conn->noop_out_timeout) {
log_error("Invalid timeo.noop_out_timeout. Must be greater "
  "than zero. Using default %d.\n",
  DEF_NOOP_OUT_TIMEO);
conn->noop_out_timeout = DEF_NOOP_OUT_TIMEO;
}

if (conn->noop_out_timeout && !conn->noop_out_interval) {
log_error("Invalid timeo.noop_out_interval. Must be greater "
  "than zero. Using default %d.\n",
  DEF_NOOP_OUT_INTERVAL);
conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL;
}

//複製參數
iscsi_copy_operational_params(conn, &session->nrec.session.iscsi,
      &conn_rec->iscsi);

/* TCP options */
conn->tcp_window_size = conn_rec->tcp.window_size;
/* FIXME: type_of_service */

/* resolve the string address to an IP address */
err = iscsi_setup_portal(conn, conn_rec->address, conn_rec->port);
if (err)
return err;
return 0;
}

if (iscsi_host_set_net_params(&rec->iface, session)) {
__session_destroy(session);
return ISCSI_ERR_LOGIN;
}

if (gettimeofday(&conn->initial_connect_time, NULL))
log_error("Could not get initial connect time. If "
  "login errors iscsid may give up the initial "
  "login early. You should manually login.");

conn->state = ISCSI_CONN_STATE_XPT_WAIT;
qtask->rsp.command = MGMT_IPC_SESSION_LOGIN;
qtask->rsp.err = ISCSI_SUCCESS;

//連接
if (iscsi_conn_connect(conn, qtask)) {
log_debug(4, "Initial connect failed. Waiting %u seconds "
  "before trying to reconnect.\n",
  ISCSI_CONN_ERR_REOPEN_DELAY);
queue_delayed_reopen(qtask, ISCSI_CONN_ERR_REOPEN_DELAY);
}

return ISCSI_SUCCESS;

到這裏,會話就建立了,然後就是handle函數向請求返回結果了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章