diff options
Diffstat (limited to 'drivers/target/iscsi/iscsi_target_parameters.c')
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_parameters.c | 200 |
1 files changed, 153 insertions, 47 deletions
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c index ed5241e7f12..02f9de26f38 100644 --- a/drivers/target/iscsi/iscsi_target_parameters.c +++ b/drivers/target/iscsi/iscsi_target_parameters.c @@ -1,9 +1,7 @@ /******************************************************************************* * This file contains main functions related to iSCSI Parameter negotiation. * - * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. - * - * Licensed to the Linux Foundation under the General Public License (GPL) version 2. + * (c) Copyright 2007-2013 Datera, Inc. * * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> * @@ -59,7 +57,7 @@ int iscsi_login_tx_data( char *text_buf, int text_length) { - int length, tx_sent; + int length, tx_sent, iov_cnt = 1; struct kvec iov[2]; length = (ISCSI_HDR_LEN + text_length); @@ -67,8 +65,12 @@ int iscsi_login_tx_data( memset(&iov[0], 0, 2 * sizeof(struct kvec)); iov[0].iov_len = ISCSI_HDR_LEN; iov[0].iov_base = pdu_buf; - iov[1].iov_len = text_length; - iov[1].iov_base = text_buf; + + if (text_buf && text_length) { + iov[1].iov_len = text_length; + iov[1].iov_base = text_buf; + iov_cnt++; + } /* * Initial Marker-less Interval. @@ -77,7 +79,7 @@ int iscsi_login_tx_data( */ conn->if_marker += length; - tx_sent = tx_data(conn, &iov[0], 2, length); + tx_sent = tx_data(conn, &iov[0], iov_cnt, length); if (tx_sent != length) { pr_err("tx_data returned %d, expecting %d.\n", tx_sent, length); @@ -154,22 +156,18 @@ static struct iscsi_param *iscsi_set_default_param(struct iscsi_param_list *para } INIT_LIST_HEAD(¶m->p_list); - param->name = kzalloc(strlen(name) + 1, GFP_KERNEL); + param->name = kstrdup(name, GFP_KERNEL); if (!param->name) { pr_err("Unable to allocate memory for parameter name.\n"); goto out; } - param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); + param->value = kstrdup(value, GFP_KERNEL); if (!param->value) { pr_err("Unable to allocate memory for parameter value.\n"); goto out; } - memcpy(param->name, name, strlen(name)); - param->name[strlen(name)] = '\0'; - memcpy(param->value, value, strlen(value)); - param->value[strlen(value)] = '\0'; param->phase = phase; param->scope = scope; param->sender = sender; @@ -334,6 +332,13 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) if (!param) goto out; + param = iscsi_set_default_param(pl, MAXXMITDATASEGMENTLENGTH, + INITIAL_MAXXMITDATASEGMENTLENGTH, + PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, + TYPERANGE_512_TO_16777215, USE_ALL); + if (!param) + goto out; + param = iscsi_set_default_param(pl, MAXRECVDATASEGMENTLENGTH, INITIAL_MAXRECVDATASEGMENTLENGTH, PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, @@ -426,6 +431,28 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) TYPERANGE_MARKINT, USE_INITIAL_ONLY); if (!param) goto out; + /* + * Extra parameters for ISER from RFC-5046 + */ + param = iscsi_set_default_param(pl, RDMAEXTENSIONS, INITIAL_RDMAEXTENSIONS, + PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, + TYPERANGE_BOOL_AND, USE_LEADING_ONLY); + if (!param) + goto out; + + param = iscsi_set_default_param(pl, INITIATORRECVDATASEGMENTLENGTH, + INITIAL_INITIATORRECVDATASEGMENTLENGTH, + PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, + TYPERANGE_512_TO_16777215, USE_ALL); + if (!param) + goto out; + + param = iscsi_set_default_param(pl, TARGETRECVDATASEGMENTLENGTH, + INITIAL_TARGETRECVDATASEGMENTLENGTH, + PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, + TYPERANGE_512_TO_16777215, USE_ALL); + if (!param) + goto out; *param_list_ptr = pl; return 0; @@ -435,19 +462,23 @@ out: } int iscsi_set_keys_to_negotiate( - int sessiontype, - struct iscsi_param_list *param_list) + struct iscsi_param_list *param_list, + bool iser) { struct iscsi_param *param; + param_list->iser = iser; + list_for_each_entry(param, ¶m_list->param_list, p_list) { param->state = 0; if (!strcmp(param->name, AUTHMETHOD)) { SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, HEADERDIGEST)) { - SET_PSTATE_NEGOTIATE(param); + if (!iser) + SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, DATADIGEST)) { - SET_PSTATE_NEGOTIATE(param); + if (!iser) + SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, MAXCONNECTIONS)) { SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, TARGETNAME)) { @@ -466,7 +497,10 @@ int iscsi_set_keys_to_negotiate( } else if (!strcmp(param->name, IMMEDIATEDATA)) { SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { - SET_PSTATE_NEGOTIATE(param); + if (!iser) + SET_PSTATE_NEGOTIATE(param); + } else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { + continue; } else if (!strcmp(param->name, MAXBURSTLENGTH)) { SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, FIRSTBURSTLENGTH)) { @@ -493,6 +527,15 @@ int iscsi_set_keys_to_negotiate( SET_PSTATE_NEGOTIATE(param); } else if (!strcmp(param->name, OFMARKINT)) { SET_PSTATE_NEGOTIATE(param); + } else if (!strcmp(param->name, RDMAEXTENSIONS)) { + if (iser) + SET_PSTATE_NEGOTIATE(param); + } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) { + if (iser) + SET_PSTATE_NEGOTIATE(param); + } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) { + if (iser) + SET_PSTATE_NEGOTIATE(param); } } @@ -535,6 +578,12 @@ int iscsi_set_keys_irrelevant_for_discovery( param->state &= ~PSTATE_NEGOTIATE; else if (!strcmp(param->name, OFMARKINT)) param->state &= ~PSTATE_NEGOTIATE; + else if (!strcmp(param->name, RDMAEXTENSIONS)) + param->state &= ~PSTATE_NEGOTIATE; + else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) + param->state &= ~PSTATE_NEGOTIATE; + else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) + param->state &= ~PSTATE_NEGOTIATE; } return 0; @@ -626,11 +675,8 @@ void iscsi_release_param_list(struct iscsi_param_list *param_list) list_del(¶m->p_list); kfree(param->name); - param->name = NULL; kfree(param->value); - param->value = NULL; kfree(param); - param = NULL; } iscsi_release_extra_responses(param_list); @@ -662,7 +708,7 @@ int iscsi_extract_key_value(char *textbuf, char **key, char **value) { *value = strchr(textbuf, '='); if (!*value) { - pr_err("Unable to locate \"=\" seperator for key," + pr_err("Unable to locate \"=\" separator for key," " ignoring request.\n"); return -1; } @@ -678,15 +724,12 @@ int iscsi_update_param_value(struct iscsi_param *param, char *value) { kfree(param->value); - param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); + param->value = kstrdup(value, GFP_KERNEL); if (!param->value) { pr_err("Unable to allocate memory for value.\n"); - return -1; + return -ENOMEM; } - memcpy(param->value, value, strlen(value)); - param->value[strlen(value)] = '\0'; - pr_debug("iSCSI Parameter updated to %s=%s\n", param->name, param->value); return 0; @@ -713,9 +756,9 @@ static int iscsi_add_notunderstood_response( } INIT_LIST_HEAD(&extra_response->er_list); - strncpy(extra_response->key, key, strlen(key) + 1); - strncpy(extra_response->value, NOTUNDERSTOOD, - strlen(NOTUNDERSTOOD) + 1); + strlcpy(extra_response->key, key, sizeof(extra_response->key)); + strlcpy(extra_response->value, NOTUNDERSTOOD, + sizeof(extra_response->value)); list_add_tail(&extra_response->er_list, ¶m_list->extra_response_list); @@ -1056,7 +1099,8 @@ out: return proposer_values; } -static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value) +static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value, + struct iscsi_conn *conn) { u8 acceptor_boolean_value = 0, proposer_boolean_value = 0; char *negoitated_value = NULL; @@ -1095,11 +1139,11 @@ static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value) SET_PSTATE_REPLY_OPTIONAL(param); } } else if (IS_TYPE_NUMBER(param)) { - char *tmpptr, buf[10]; + char *tmpptr, buf[11]; u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0); u32 proposer_value = simple_strtoul(value, &tmpptr, 0); - memset(buf, 0, 10); + memset(buf, 0, sizeof(buf)); if (!strcmp(param->name, MAXCONNECTIONS) || !strcmp(param->name, MAXBURSTLENGTH) || @@ -1131,8 +1175,35 @@ static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value) return -1; } - if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) - SET_PSTATE_REPLY_OPTIONAL(param); + if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { + struct iscsi_param *param_mxdsl; + unsigned long long tmp; + int rc; + + rc = kstrtoull(param->value, 0, &tmp); + if (rc < 0) + return -1; + + conn->conn_ops->MaxRecvDataSegmentLength = tmp; + pr_debug("Saving op->MaxRecvDataSegmentLength from" + " original initiator received value: %u\n", + conn->conn_ops->MaxRecvDataSegmentLength); + + param_mxdsl = iscsi_find_param_from_key( + MAXXMITDATASEGMENTLENGTH, + conn->param_list); + if (!param_mxdsl) + return -1; + + rc = iscsi_update_param_value(param, + param_mxdsl->value); + if (rc < 0) + return -1; + + pr_debug("Updated %s to target MXDSL value: %s\n", + param->name, param->value); + } + } else if (IS_TYPE_NUMBER_RANGE(param)) { negoitated_value = iscsi_get_value_from_number_range( param, value); @@ -1269,7 +1340,7 @@ static int iscsi_check_value(struct iscsi_param *param, char *value) comma_ptr = strchr(value, ','); if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) { - pr_err("Detected value seperator \",\", but" + pr_err("Detected value separator \",\", but" " key \"%s\" does not allow a value list," " protocol error.\n", param->name); return -1; @@ -1395,6 +1466,7 @@ static struct iscsi_param *iscsi_check_key( break; case PHASE_OPERATIONAL: pr_debug("Operational phase.\n"); + break; default: pr_debug("Unknown phase.\n"); } @@ -1475,8 +1547,8 @@ static int iscsi_enforce_integrity_rules( FirstBurstLength = simple_strtoul(param->value, &tmpptr, 0); if (FirstBurstLength > MaxBurstLength) { - char tmpbuf[10]; - memset(tmpbuf, 0, 10); + char tmpbuf[11]; + memset(tmpbuf, 0, sizeof(tmpbuf)); sprintf(tmpbuf, "%u", MaxBurstLength); if (iscsi_update_param_value(param, tmpbuf)) return -1; @@ -1526,13 +1598,14 @@ int iscsi_decode_text_input( u8 sender, char *textbuf, u32 length, - struct iscsi_param_list *param_list) + struct iscsi_conn *conn) { + struct iscsi_param_list *param_list = conn->param_list; char *tmpbuf, *start = NULL, *end = NULL; tmpbuf = kzalloc(length + 1, GFP_KERNEL); if (!tmpbuf) { - pr_err("Unable to allocate memory for tmpbuf.\n"); + pr_err("Unable to allocate %u + 1 bytes for tmpbuf.\n", length); return -1; } @@ -1554,8 +1627,6 @@ int iscsi_decode_text_input( if (phase & PHASE_SECURITY) { if (iscsi_check_for_auth_key(key) > 0) { - char *tmpptr = key + strlen(key); - *tmpptr = '='; kfree(tmpbuf); return 1; } @@ -1585,7 +1656,7 @@ int iscsi_decode_text_input( } SET_PSTATE_RESPONSE_GOT(param); } else { - if (iscsi_check_acceptor_state(param, value) < 0) { + if (iscsi_check_acceptor_state(param, value, conn) < 0) { kfree(tmpbuf); return -1; } @@ -1720,6 +1791,18 @@ void iscsi_set_connection_parameters( pr_debug("---------------------------------------------------" "---------------\n"); list_for_each_entry(param, ¶m_list->param_list, p_list) { + /* + * Special case to set MAXXMITDATASEGMENTLENGTH from the + * target requested MaxRecvDataSegmentLength, even though + * this key is not sent over the wire. + */ + if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { + ops->MaxXmitDataSegmentLength = + simple_strtoul(param->value, &tmpptr, 0); + pr_debug("MaxXmitDataSegmentLength: %s\n", + param->value); + } + if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param)) continue; if (!strcmp(param->name, AUTHMETHOD)) { @@ -1734,10 +1817,13 @@ void iscsi_set_connection_parameters( pr_debug("DataDigest: %s\n", param->value); } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { - ops->MaxRecvDataSegmentLength = - simple_strtoul(param->value, &tmpptr, 0); - pr_debug("MaxRecvDataSegmentLength: %s\n", - param->value); + /* + * At this point iscsi_check_acceptor_state() will have + * set ops->MaxRecvDataSegmentLength from the original + * initiator provided value. + */ + pr_debug("MaxRecvDataSegmentLength: %u\n", + ops->MaxRecvDataSegmentLength); } else if (!strcmp(param->name, OFMARKER)) { ops->OFMarker = !strcmp(param->value, YES); pr_debug("OFMarker: %s\n", @@ -1756,6 +1842,22 @@ void iscsi_set_connection_parameters( simple_strtoul(param->value, &tmpptr, 0); pr_debug("IFMarkInt: %s\n", param->value); + } else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) { + ops->InitiatorRecvDataSegmentLength = + simple_strtoul(param->value, &tmpptr, 0); + pr_debug("InitiatorRecvDataSegmentLength: %s\n", + param->value); + ops->MaxRecvDataSegmentLength = + ops->InitiatorRecvDataSegmentLength; + pr_debug("Set MRDSL from InitiatorRecvDataSegmentLength\n"); + } else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) { + ops->TargetRecvDataSegmentLength = + simple_strtoul(param->value, &tmpptr, 0); + pr_debug("TargetRecvDataSegmentLength: %s\n", + param->value); + ops->MaxXmitDataSegmentLength = + ops->TargetRecvDataSegmentLength; + pr_debug("Set MXDSL from TargetRecvDataSegmentLength\n"); } } pr_debug("----------------------------------------------------" @@ -1868,6 +1970,10 @@ void iscsi_set_session_parameters( ops->SessionType = !strcmp(param->value, DISCOVERY); pr_debug("SessionType: %s\n", param->value); + } else if (!strcmp(param->name, RDMAEXTENSIONS)) { + ops->RDMAExtensions = !strcmp(param->value, YES); + pr_debug("RDMAExtensions: %s\n", + param->value); } } pr_debug("----------------------------------------------------" |
