// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of the Emulex Linux Device Driver for Enterprise iSCSI * Host Bus Adapters. Refer to the README file included with this package * for driver version and adapter compatibility. * * Copyright (c) 2018 Broadcom. All Rights Reserved. * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. * * Contact Information: * linux-drivers@broadcom.com
*/
/** * beiscsi_iface_config_vlan()- Set the VLAN TAG * @shost: Scsi Host for the driver instance * @iface_param: Interface paramters * * Set the VLAN TAG for the adapter or disable * the VLAN config * * returns * Success: 0 * Failure: Non-Zero Value
**/ staticint
beiscsi_iface_config_vlan(struct Scsi_Host *shost, struct iscsi_iface_param_info *iface_param)
{ struct beiscsi_hba *phba = iscsi_host_priv(shost); int ret = -EPERM;
switch (iface_param->param) { case ISCSI_NET_PARAM_VLAN_ENABLED:
ret = 0; if (iface_param->value[0] != ISCSI_VLAN_ENABLE)
ret = beiscsi_if_set_vlan(phba, BEISCSI_VLAN_DISABLE); break; case ISCSI_NET_PARAM_VLAN_TAG:
ret = beiscsi_if_set_vlan(phba,
*((uint16_t *)iface_param->value)); break;
} return ret;
}
nla_for_each_attr(attrib, data, dt_len, rm_len) { /* ignore nla_type as it is never used */ if (nla_len(attrib) < sizeof(*iface_param)) return -EINVAL;
iface_param = nla_data(attrib);
if (iface_param->param_type != ISCSI_NET_PARAM) continue;
/* * BE2ISCSI only supports 1 interface
*/ if (iface_param->iface_num) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Invalid iface_num %d." "Only iface_num 0 is supported.\n",
iface_param->iface_num);
ret = -EPERM; switch (iface_param->param) { case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_TAG:
ret = beiscsi_iface_config_vlan(shost, iface_param); break; default: switch (iface_param->iface_type) { case ISCSI_IFACE_TYPE_IPV4:
ret = beiscsi_iface_config_ipv4(shost,
iface_param,
data, dt_len); break; case ISCSI_IFACE_TYPE_IPV6:
ret = beiscsi_iface_config_ipv6(shost,
iface_param,
data, dt_len); break;
}
}
if (ret == -EPERM) {
__beiscsi_log(phba, KERN_ERR, "BS_%d : %s.0 set param %d not permitted",
(iface_param->iface_type ==
ISCSI_IFACE_TYPE_IPV4) ? "ipv4" : "ipv6",
iface_param->param);
ret = 0;
} if (ret) break;
}
return ret;
}
staticint __beiscsi_iface_get_param(struct beiscsi_hba *phba, struct iscsi_iface *iface, int param, char *buf)
{ struct be_cmd_get_if_info_resp *if_info; int len, ip_type = BEISCSI_IP_TYPE_V4;
if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
ip_type = BEISCSI_IP_TYPE_V6;
len = beiscsi_if_get_info(phba, ip_type, &if_info); if (len) return len;
switch (param) { case ISCSI_NET_PARAM_IPV4_ADDR:
len = sprintf(buf, "%pI4\n", if_info->ip_addr.addr); break; case ISCSI_NET_PARAM_IPV6_ADDR:
len = sprintf(buf, "%pI6\n", if_info->ip_addr.addr); break; case ISCSI_NET_PARAM_IPV4_BOOTPROTO: if (!if_info->dhcp_state)
len = sprintf(buf, "static\n"); else
len = sprintf(buf, "dhcp\n"); break; case ISCSI_NET_PARAM_IPV4_SUBNET:
len = sprintf(buf, "%pI4\n", if_info->ip_addr.subnet_mask); break; case ISCSI_NET_PARAM_VLAN_ENABLED:
len = sprintf(buf, "%s\n",
(if_info->vlan_priority == BEISCSI_VLAN_DISABLE) ? "disable" : "enable"); break; case ISCSI_NET_PARAM_VLAN_ID: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE)
len = -EINVAL; else
len = sprintf(buf, "%d\n",
(if_info->vlan_priority &
ISCSI_MAX_VLAN_ID)); break; case ISCSI_NET_PARAM_VLAN_PRIORITY: if (if_info->vlan_priority == BEISCSI_VLAN_DISABLE)
len = -EINVAL; else
len = sprintf(buf, "%d\n",
((if_info->vlan_priority >> 13) &
ISCSI_MAX_VLAN_PRIORITY)); break; default:
WARN_ON(1);
}
kfree(if_info); return len;
}
int beiscsi_iface_get_param(struct iscsi_iface *iface, enum iscsi_param_type param_type, int param, char *buf)
{ struct Scsi_Host *shost = iscsi_iface_to_shost(iface); struct beiscsi_hba *phba = iscsi_host_priv(shost); struct be_cmd_get_def_gateway_resp gateway; int len = -EPERM;
if (param_type != ISCSI_NET_PARAM) return 0; if (!beiscsi_hba_is_online(phba)) {
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return -EBUSY;
}
switch (param) { case ISCSI_NET_PARAM_IPV4_ADDR: case ISCSI_NET_PARAM_IPV4_SUBNET: case ISCSI_NET_PARAM_IPV4_BOOTPROTO: case ISCSI_NET_PARAM_IPV6_ADDR: case ISCSI_NET_PARAM_VLAN_ENABLED: case ISCSI_NET_PARAM_VLAN_ID: case ISCSI_NET_PARAM_VLAN_PRIORITY:
len = __beiscsi_iface_get_param(phba, iface, param, buf); break; case ISCSI_NET_PARAM_IFACE_ENABLE: if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4)
len = sprintf(buf, "%s\n",
phba->ipv4_iface ? "enable" : "disable"); elseif (iface->iface_type == ISCSI_IFACE_TYPE_IPV6)
len = sprintf(buf, "%s\n",
phba->ipv6_iface ? "enable" : "disable"); break; case ISCSI_NET_PARAM_IPV4_GW:
memset(&gateway, 0, sizeof(gateway));
len = beiscsi_if_get_gw(phba, BEISCSI_IP_TYPE_V4, &gateway); if (!len)
len = sprintf(buf, "%pI4\n", &gateway.ip_addr.addr); break;
}
return len;
}
/** * beiscsi_ep_get_param - get the iscsi parameter * @ep: pointer to iscsi ep * @param: parameter type identifier * @buf: buffer pointer * * returns iscsi parameter
*/ int beiscsi_ep_get_param(struct iscsi_endpoint *ep, enum iscsi_param param, char *buf)
{ struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; int len;
ret = iscsi_set_param(cls_conn, param, buf, buflen); if (ret) return ret; /* * If userspace tried to set the value to higher than we can * support override here.
*/ switch (param) { case ISCSI_PARAM_FIRST_BURST: if (session->first_burst > 8192)
session->first_burst = 8192; break; case ISCSI_PARAM_MAX_RECV_DLENGTH: if (conn->max_recv_dlength > 65536)
conn->max_recv_dlength = 65536; break; case ISCSI_PARAM_MAX_BURST: if (session->max_burst > 262144)
session->max_burst = 262144; break; case ISCSI_PARAM_MAX_XMIT_DLENGTH: if (conn->max_xmit_dlength > 65536)
conn->max_xmit_dlength = 65536;
fallthrough; default: return 0;
}
return 0;
}
/** * beiscsi_get_port_state - Get the Port State * @shost : pointer to scsi_host structure *
*/ staticvoid beiscsi_get_port_state(struct Scsi_Host *shost)
{ struct beiscsi_hba *phba = iscsi_host_priv(shost); struct iscsi_cls_host *ihost = shost->shost_data;
/* Find the ULP which has more CID available */
cid_avlbl_ulp0 = (phba->cid_array_info[BEISCSI_ULP0]) ?
BEISCSI_ULP0_AVLBL_CID(phba) : 0;
cid_avlbl_ulp1 = (phba->cid_array_info[BEISCSI_ULP1]) ?
BEISCSI_ULP1_AVLBL_CID(phba) : 0;
cid_from_ulp = (cid_avlbl_ulp0 > cid_avlbl_ulp1) ?
BEISCSI_ULP0 : BEISCSI_ULP1; /** * If iSCSI protocol is loaded only on ULP 0, and when cid_avlbl_ulp * is ZERO for both, ULP 1 is returned. * Check if ULP is loaded before getting new CID.
*/ if (!test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported)) return BE_INVALID_CID;
cid_info = phba->cid_array_info[cid_from_ulp];
cid = cid_info->cid_array[cid_info->cid_alloc]; if (!cid_info->avlbl_cids || cid == BE_INVALID_CID) {
__beiscsi_log(phba, KERN_ERR, "BS_%d : failed to get cid: available %u:%u\n",
cid_info->avlbl_cids, cid_info->cid_free); return BE_INVALID_CID;
} /* empty the slot */
cid_info->cid_array[cid_info->cid_alloc++] = BE_INVALID_CID; if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(phba, cid_from_ulp))
cid_info->cid_alloc = 0;
cid_info->avlbl_cids--; return cid;
}
/** * beiscsi_put_cid - Free the cid * @phba: The phba for which the cid is being freed * @cid: The cid to free
*/ staticvoid beiscsi_put_cid(struct beiscsi_hba *phba, unsignedshort cid)
{
uint16_t cri_index = BE_GET_CRI_FROM_CID(cid); struct hwi_wrb_context *pwrb_context; struct hwi_controller *phwi_ctrlr; struct ulp_cid_info *cid_info;
uint16_t cid_post_ulp;
beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
beiscsi_ep->phba = NULL; /* clear this to track freeing in beiscsi_ep_disconnect */
phba->ep_array[BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid)] = NULL;
/** * Check if any connection resource allocated by driver * is to be freed.This case occurs when target redirection * or connection retry is done.
**/ if (!beiscsi_ep->conn) return;
beiscsi_conn = beiscsi_ep->conn; /** * Break ep->conn link here so that completions after * this are ignored.
*/
beiscsi_ep->conn = NULL; if (beiscsi_conn->login_in_progress) {
beiscsi_free_mgmt_task_handles(beiscsi_conn,
beiscsi_conn->task);
beiscsi_conn->login_in_progress = 0;
}
}
/** * beiscsi_open_conn - Ask FW to open a TCP connection * @ep: pointer to device endpoint struct * @src_addr: The source IP address * @dst_addr: The Destination IP address * @non_blocking: blocking or non-blocking call * * Asks the FW to open a TCP connection
*/ staticint beiscsi_open_conn(struct iscsi_endpoint *ep, struct sockaddr *src_addr, struct sockaddr *dst_addr, int non_blocking)
{ struct beiscsi_endpoint *beiscsi_ep = ep->dd_data; struct beiscsi_hba *phba = beiscsi_ep->phba; struct tcp_connect_and_offload_out *ptcpcnct_out; struct be_dma_mem nonemb_cmd; unsignedint tag, req_memsize; int ret = -ENOMEM;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : In beiscsi_open_conn\n");
beiscsi_ep->ep_cid = beiscsi_get_cid(phba); if (beiscsi_ep->ep_cid == BE_INVALID_CID) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : No free cid available\n"); return ret;
}
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : In beiscsi_open_conn, ep_cid=%d\n",
beiscsi_ep->ep_cid);
/** * beiscsi_ep_connect - Ask chip to create TCP Conn * @shost: Pointer to scsi_host structure * @dst_addr: The IP address of Target * @non_blocking: blocking or non-blocking call * * This routines first asks chip to create a connection and then allocates an EP
*/ struct iscsi_endpoint *
beiscsi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, int non_blocking)
{ struct beiscsi_hba *phba; struct beiscsi_endpoint *beiscsi_ep; struct iscsi_endpoint *ep; int ret;
if (!shost) {
ret = -ENXIO;
pr_err("beiscsi_ep_connect shost is NULL\n"); return ERR_PTR(ret);
}
phba = iscsi_host_priv(shost); if (!beiscsi_hba_is_online(phba)) {
ret = -EIO;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state); return ERR_PTR(ret);
} if (!test_bit(BEISCSI_HBA_LINK_UP, &phba->state)) {
ret = -EBUSY;
beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG, "BS_%d : The Adapter Port state is Down!!!\n"); return ERR_PTR(ret);
}
ep = iscsi_create_endpoint(sizeof(struct beiscsi_endpoint)); if (!ep) {
ret = -ENOMEM; return ERR_PTR(ret);
}
beiscsi_ep = ep->dd_data;
beiscsi_ep->phba = phba;
beiscsi_ep->openiscsi_ep = ep;
ret = beiscsi_open_conn(ep, NULL, dst_addr, non_blocking); if (ret) {
beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG, "BS_%d : Failed in beiscsi_open_conn\n"); goto free_ep;
}
/** * beiscsi_ep_poll - Poll to see if connection is established * @ep: endpoint to be used * @timeout_ms: timeout specified in millisecs * * Poll to see if TCP connection established
*/ int beiscsi_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{ struct beiscsi_endpoint *beiscsi_ep = ep->dd_data;
beiscsi_log(beiscsi_ep->phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : In beiscsi_ep_poll\n");
if (beiscsi_ep->cid_vld == 1) return 1; else return 0;
}
/** * beiscsi_flush_cq()- Flush the CQ created. * @phba: ptr device priv structure. * * Before the connection resource are freed flush * all the CQ enteries
**/ staticvoid beiscsi_flush_cq(struct beiscsi_hba *phba)
{
uint16_t i; struct be_eq_obj *pbe_eq; struct hwi_controller *phwi_ctrlr; struct hwi_context_memory *phwi_context;
for (i = 0; i < phba->num_cpus; i++) {
pbe_eq = &phwi_context->be_eq[i];
irq_poll_disable(&pbe_eq->iopoll);
beiscsi_process_cq(pbe_eq, BE2_MAX_NUM_CQ_PROC);
irq_poll_enable(&pbe_eq->iopoll);
}
}
/** * beiscsi_conn_close - Invalidate and upload connection * @beiscsi_ep: pointer to device endpoint struct * * Returns 0 on success, -1 on failure.
*/ staticint beiscsi_conn_close(struct beiscsi_endpoint *beiscsi_ep)
{ struct beiscsi_hba *phba = beiscsi_ep->phba; unsignedint tag, attempts; int ret;
/** * Without successfully invalidating and uploading connection * driver can't reuse the CID so attempt more than once.
*/
attempts = 0; while (attempts++ < 3) {
tag = beiscsi_invalidate_cxn(phba, beiscsi_ep); if (tag) {
ret = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); if (!ret) break;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : invalidate conn failed cid %d\n",
beiscsi_ep->ep_cid);
}
}
/* wait for all completions to arrive, then process them */
msleep(250); /* flush CQ entries */
beiscsi_flush_cq(phba);
if (attempts > 3) return -1;
attempts = 0; while (attempts++ < 3) {
tag = beiscsi_upload_cxn(phba, beiscsi_ep); if (tag) {
ret = beiscsi_mccq_compl_wait(phba, tag, NULL, NULL); if (!ret) break;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : upload conn failed cid %d\n",
beiscsi_ep->ep_cid);
}
} if (attempts > 3) return -1;
return 0;
}
/** * beiscsi_ep_disconnect - Tears down the TCP connection * @ep: endpoint to be used * * Tears down the TCP connection
*/ void beiscsi_ep_disconnect(struct iscsi_endpoint *ep)
{ struct beiscsi_endpoint *beiscsi_ep; struct beiscsi_hba *phba;
uint16_t cri_index;
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : In beiscsi_ep_disconnect for ep_cid = %u\n",
beiscsi_ep->ep_cid);
cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid); if (!phba->ep_array[cri_index]) {
__beiscsi_log(phba, KERN_ERR, "BS_%d : ep_array at %u cid %u empty\n",
cri_index,
beiscsi_ep->ep_cid); return;
}
if (!beiscsi_hba_is_online(phba)) {
beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG, "BS_%d : HBA in error 0x%lx\n", phba->state);
} else { /** * Make CID available even if close fails. * If not freed, FW might fail open using the CID.
*/ if (beiscsi_conn_close(beiscsi_ep) < 0)
__beiscsi_log(phba, KERN_ERR, "BS_%d : close conn failed cid %d\n",
beiscsi_ep->ep_cid);
}
beiscsi_free_ep(beiscsi_ep); if (!phba->conn_table[cri_index])
__beiscsi_log(phba, KERN_ERR, "BS_%d : conn_table empty at %u: cid %u\n",
cri_index, beiscsi_ep->ep_cid);
phba->conn_table[cri_index] = NULL;
iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
}
umode_t beiscsi_attr_is_visible(int param_type, int param)
{ switch (param_type) { case ISCSI_NET_PARAM: switch (param) { case ISCSI_NET_PARAM_IFACE_ENABLE: case ISCSI_NET_PARAM_IPV4_ADDR: case ISCSI_NET_PARAM_IPV4_SUBNET: case ISCSI_NET_PARAM_IPV4_BOOTPROTO: case ISCSI_NET_PARAM_IPV4_GW: case ISCSI_NET_PARAM_IPV6_ADDR: case ISCSI_NET_PARAM_VLAN_ID: case ISCSI_NET_PARAM_VLAN_PRIORITY: case ISCSI_NET_PARAM_VLAN_ENABLED: return S_IRUGO; default: return 0;
} case ISCSI_HOST_PARAM: switch (param) { case ISCSI_HOST_PARAM_HWADDRESS: case ISCSI_HOST_PARAM_INITIATOR_NAME: case ISCSI_HOST_PARAM_PORT_STATE: case ISCSI_HOST_PARAM_PORT_SPEED: return S_IRUGO; default: return 0;
} case ISCSI_PARAM: switch (param) { case ISCSI_PARAM_MAX_RECV_DLENGTH: case ISCSI_PARAM_MAX_XMIT_DLENGTH: case ISCSI_PARAM_HDRDGST_EN: case ISCSI_PARAM_DATADGST_EN: case ISCSI_PARAM_CONN_ADDRESS: case ISCSI_PARAM_CONN_PORT: case ISCSI_PARAM_EXP_STATSN: case ISCSI_PARAM_PERSISTENT_ADDRESS: case ISCSI_PARAM_PERSISTENT_PORT: case ISCSI_PARAM_PING_TMO: case ISCSI_PARAM_RECV_TMO: case ISCSI_PARAM_INITIAL_R2T_EN: case ISCSI_PARAM_MAX_R2T: case ISCSI_PARAM_IMM_DATA_EN: case ISCSI_PARAM_FIRST_BURST: case ISCSI_PARAM_MAX_BURST: case ISCSI_PARAM_PDU_INORDER_EN: case ISCSI_PARAM_DATASEQ_INORDER_EN: case ISCSI_PARAM_ERL: case ISCSI_PARAM_TARGET_NAME: case ISCSI_PARAM_TPGT: case ISCSI_PARAM_USERNAME: case ISCSI_PARAM_PASSWORD: case ISCSI_PARAM_USERNAME_IN: case ISCSI_PARAM_PASSWORD_IN: case ISCSI_PARAM_FAST_ABORT: case ISCSI_PARAM_ABORT_TMO: case ISCSI_PARAM_LU_RESET_TMO: case ISCSI_PARAM_IFACE_NAME: case ISCSI_PARAM_INITIATOR_NAME: return S_IRUGO; default: return 0;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.