/* * iSCSI Initiator over iSER Data-Path * * Copyright (C) 2004 Dmitry Yusupov * Copyright (C) 2004 Alex Aizman * Copyright (C) 2005 Mike Christie * Copyright (c) 2005, 2006 Voltaire, Inc. All rights reserved. * Copyright (c) 2013-2014 Mellanox Technologies. All rights reserved. * maintained by openib-general@openib.org * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Credits: * Christoph Hellwig * FUJITA Tomonori * Arne Redlich * Zhenyu Wang * Modified by: * Erez Zilber
*/
MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
staticunsignedint iscsi_max_lun = 512;
module_param_cb(max_lun, &iscsi_iser_size_ops, &iscsi_max_lun, S_IRUGO);
MODULE_PARM_DESC(max_lun, "Max LUNs to allow per session, should > 0 (default:512)");
unsignedint iser_max_sectors = ISER_DEF_MAX_SECTORS;
module_param_cb(max_sectors, &iscsi_iser_size_ops, &iser_max_sectors,
S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(max_sectors, "Max number of sectors in a single scsi command, should > 0 (default:1024)");
bool iser_always_reg = true;
module_param_named(always_register, iser_always_reg, bool, S_IRUGO);
MODULE_PARM_DESC(always_register, "Always register memory, even for continuous memory regions (default:true)");
staticint iscsi_iser_set(constchar *val, conststruct kernel_param *kp)
{ int ret; unsignedint n = 0;
ret = kstrtouint(val, 10, &n); if (ret != 0 || n == 0) return -EINVAL;
return param_set_uint(val, kp);
}
/* * iscsi_iser_recv() - Process a successful recv completion * @conn: iscsi connection * @hdr: iscsi header * @rx_data: buffer containing receive data payload * @rx_data_len: length of rx_data * * Notes: In case of data length errors or iscsi PDU completion failures * this routine will signal iscsi layer of connection failure.
*/ void iscsi_iser_recv(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
{ int rc = 0; int datalen;
/** * iser_initialize_task_headers() - Initialize task headers * @task: iscsi task * @tx_desc: iser tx descriptor * * Notes: * This routine may race with iser teardown flow for scsi * error handling TMFs. So for TMF we should acquire the * state mutex to avoid dereferencing the IB device which * may have already been terminated.
*/ int iser_initialize_task_headers(struct iscsi_task *task, struct iser_tx_desc *tx_desc)
{ struct iser_conn *iser_conn = task->conn->dd_data; struct iser_device *device = iser_conn->ib_conn.device; struct iscsi_iser_task *iser_task = task->dd_data;
u64 dma_addr;
if (unlikely(iser_conn->state != ISER_CONN_UP)) return -ENODEV;
/** * iscsi_iser_mtask_xmit() - xmit management (immediate) task * @conn: iscsi connection * @task: task management task * * Notes: * The function can return -EAGAIN in which case caller must * call it again later, or recover. '0' return code means successful * xmit. *
**/ staticint iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
{
iser_dbg("mtask xmit [cid %d itt 0x%x]\n", conn->id, task->itt);
/* since iser xmits control with zero copy, tasks can not be recycled * right after sending them. * The recycling scheme is based on whether a response is expected * - if yes, the task is recycled at iscsi_complete_pdu * - if no, the task is recycled at iser_snd_completion
*/ return iser_send_control(conn, task);
}
/* Send data-out PDUs while there's still unsolicited data to send */ while (iscsi_task_has_unsol_data(task)) {
iscsi_prep_data_out_pdu(task, r2t, &hdr);
iser_dbg("Sending data-out: itt 0x%x, data count %d\n",
hdr.itt, r2t->data_count);
/* the buffer description has been passed with the command */ /* Send the command */
error = iser_send_data_out(conn, task, &hdr); if (error) {
r2t->datasn--; goto iscsi_iser_task_xmit_unsol_data_exit;
}
r2t->sent += r2t->data_count;
iser_dbg("Need to send %d more as data-out PDUs\n",
r2t->data_length - r2t->sent);
}
/** * iscsi_iser_check_protection() - check protection information status of task. * @task: iscsi task * @sector: error sector if exsists (output) * * Return: zero if no data-integrity errors have occurred * 0x1: data-integrity error occurred in the guard-block * 0x2: data-integrity error occurred in the reference tag * 0x3: data-integrity error occurred in the application tag * * In addition the error sector is marked.
*/ static u8 iscsi_iser_check_protection(struct iscsi_task *task, sector_t *sector)
{ struct iscsi_iser_task *iser_task = task->dd_data; enum iser_data_dir dir = iser_task->dir[ISER_DIR_IN] ?
ISER_DIR_IN : ISER_DIR_OUT;
/* * due to issues with the login code re iser sematics * this not set in iscsi_conn_setup - FIXME
*/
conn->max_recv_dlength = ISER_RECV_DATA_SEG_LEN;
return cls_conn;
}
/** * iscsi_iser_conn_bind() - bind iscsi and iser connection structures * @cls_session: iscsi class session * @cls_conn: iscsi class connection * @transport_eph: transport end-point handle * @is_leading: indicate if this is the session leading connection (MCS) * * Return: zero on success, $error if iscsi_conn_bind fails and * -EINVAL in case end-point doesn't exists anymore or iser connection * state is not UP (teardown already started).
*/ staticint iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn,
uint64_t transport_eph, int is_leading)
{ struct iscsi_conn *conn = cls_conn->dd_data; struct iser_conn *iser_conn; struct iscsi_endpoint *ep; int error;
error = iscsi_conn_bind(cls_session, cls_conn, is_leading); if (error) return error;
/* the transport ep handle comes from user space so it must be
* verified against the global ib connections list */
ep = iscsi_lookup_endpoint(transport_eph); if (!ep) {
iser_err("can't bind eph %llx\n",
(unsignedlonglong)transport_eph); return -EINVAL;
}
iser_conn = ep->dd_data;
mutex_lock(&iser_conn->state_mutex); if (iser_conn->state != ISER_CONN_UP) {
error = -EINVAL;
iser_err("iser_conn %p state is %d, teardown started\n",
iser_conn, iser_conn->state); goto out;
}
error = iser_alloc_rx_descriptors(iser_conn, conn->session); if (error) goto out;
/* binds the iSER connection retrieved from the previously * connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */
iser_info("binding iscsi conn %p to iser_conn %p\n", conn, iser_conn);
/** * iscsi_iser_conn_start() - start iscsi-iser connection * @cls_conn: iscsi class connection * * Notes: Here iser intialize (or re-initialize) stop_completion as * from this point iscsi must call conn_stop in session/connection * teardown so iser transport must wait for it.
*/ staticint iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
{ struct iscsi_conn *iscsi_conn; struct iser_conn *iser_conn;
/** * iscsi_iser_conn_stop() - stop iscsi-iser connection * @cls_conn: iscsi class connection * @flag: indicate if recover or terminate (passed as is) * * Notes: Calling iscsi_conn_stop might theoretically race with * DEVICE_REMOVAL event and dereference a previously freed RDMA device * handle, so we call it under iser the state lock to protect against * this kind of race.
*/ staticvoid iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{ struct iscsi_conn *conn = cls_conn->dd_data; struct iser_conn *iser_conn = conn->dd_data;
/* * Userspace may have goofed up and not bound the connection or * might have only partially setup the connection.
*/ if (iser_conn) {
mutex_lock(&iser_conn->state_mutex);
mutex_lock(&unbind_iser_conn_mutex);
iser_conn_terminate(iser_conn);
iscsi_conn_stop(cls_conn, flag);
staticinlineunsignedint iser_dif_prot_caps(int prot_caps)
{ int ret = 0;
if (prot_caps & IB_PROT_T10DIF_TYPE_1)
ret |= SHOST_DIF_TYPE1_PROTECTION |
SHOST_DIX_TYPE0_PROTECTION |
SHOST_DIX_TYPE1_PROTECTION; if (prot_caps & IB_PROT_T10DIF_TYPE_2)
ret |= SHOST_DIF_TYPE2_PROTECTION |
SHOST_DIX_TYPE2_PROTECTION; if (prot_caps & IB_PROT_T10DIF_TYPE_3)
ret |= SHOST_DIF_TYPE3_PROTECTION |
SHOST_DIX_TYPE3_PROTECTION;
return ret;
}
/** * iscsi_iser_session_create() - create an iscsi-iser session * @ep: iscsi end-point handle * @cmds_max: maximum commands in this session * @qdepth: session command queue depth * @initial_cmdsn: initiator command sequnce number * * Allocates and adds a scsi host, expose DIF supprot if * exists, and sets up an iscsi session.
*/ staticstruct iscsi_cls_session *
iscsi_iser_session_create(struct iscsi_endpoint *ep,
uint16_t cmds_max, uint16_t qdepth,
uint32_t initial_cmdsn)
{ struct iscsi_cls_session *cls_session; struct Scsi_Host *shost; struct iser_conn *iser_conn = NULL; struct ib_conn *ib_conn; struct ib_device *ib_dev;
u32 max_fr_sectors;
/* * older userspace tools (before 2.0-870) did not pass us * the leading conn's ep so this will be NULL;
*/ if (ep) {
iser_conn = ep->dd_data;
shost->sg_tablesize = iser_conn->scsi_sg_tablesize;
shost->can_queue = min_t(u16, cmds_max, iser_conn->max_cmds);
mutex_lock(&iser_conn->state_mutex); if (iser_conn->state != ISER_CONN_UP) {
iser_err("iser conn %p already started teardown\n",
iser_conn);
mutex_unlock(&iser_conn->state_mutex); goto free_host;
}
/** * iscsi_iser_ep_connect() - Initiate iSER connection establishment * @shost: scsi_host * @dst_addr: destination address * @non_blocking: indicate if routine can block * * Allocate an iscsi endpoint, an iser_conn structure and bind them. * After that start RDMA connection establishment via rdma_cm. We * don't allocate iser_conn embedded in iscsi_endpoint since in teardown * the endpoint will be destroyed at ep_disconnect while iser_conn will * cleanup its resources asynchronuously. * * Return: iscsi_endpoint created by iscsi layer or ERR_PTR(error) * if fails.
*/ staticstruct iscsi_endpoint *iscsi_iser_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, int non_blocking)
{ int err; struct iser_conn *iser_conn; struct iscsi_endpoint *ep;
ep = iscsi_create_endpoint(0); if (!ep) return ERR_PTR(-ENOMEM);
/** * iscsi_iser_ep_poll() - poll for iser connection establishment to complete * @ep: iscsi endpoint (created at ep_connect) * @timeout_ms: polling timeout allowed in ms. * * This routine boils down to waiting for up_completion signaling * that cma_id got CONNECTED event. * * Return: 1 if succeeded in connection establishment, 0 if timeout expired * (libiscsi will retry will kick in) or -1 if interrupted by signal * or more likely iser connection state transitioned to TEMINATING or * DOWN during the wait period.
*/ staticint iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{ struct iser_conn *iser_conn = ep->dd_data; int rc;
rc = wait_for_completion_interruptible_timeout(&iser_conn->up_completion,
msecs_to_jiffies(timeout_ms)); /* if conn establishment failed, return error code to iscsi */ if (rc == 0) {
mutex_lock(&iser_conn->state_mutex); if (iser_conn->state == ISER_CONN_TERMINATING ||
iser_conn->state == ISER_CONN_DOWN)
rc = -1;
mutex_unlock(&iser_conn->state_mutex);
}
if (rc > 0) return 1; /* success, this is the equivalent of EPOLLOUT */ elseif (!rc) return 0; /* timeout */ else return rc; /* signal */
}
/** * iscsi_iser_ep_disconnect() - Initiate connection teardown process * @ep: iscsi endpoint handle * * This routine is not blocked by iser and RDMA termination process * completion as we queue a deffered work for iser/RDMA destruction * and cleanup or actually call it immediately in case we didn't pass * iscsi conn bind/start stage, thus it is safe.
*/ staticvoid iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
{ struct iser_conn *iser_conn = ep->dd_data;
/* * if iser_conn and iscsi_conn are bound, we must wait for * iscsi_conn_stop and flush errors completion before freeing * the iser resources. Otherwise we are safe to free resources * immediately.
*/ if (iser_conn->iscsi_conn) {
INIT_WORK(&iser_conn->release_work, iser_release_work);
queue_work(release_wq, &iser_conn->release_work);
mutex_unlock(&iser_conn->state_mutex);
} else {
iser_conn->state = ISER_CONN_DOWN;
mutex_unlock(&iser_conn->state_mutex);
iser_conn_release(iser_conn);
}
iscsi_destroy_endpoint(ep);
}
static umode_t iser_attr_is_visible(int param_type, int param)
{ switch (param_type) { case ISCSI_HOST_PARAM: switch (param) { case ISCSI_HOST_PARAM_NETDEV_NAME: case ISCSI_HOST_PARAM_HWADDRESS: case ISCSI_HOST_PARAM_INITIATOR_NAME: 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_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_TGT_RESET_TMO: case ISCSI_PARAM_IFACE_NAME: case ISCSI_PARAM_INITIATOR_NAME: case ISCSI_PARAM_DISCOVERY_SESS: return S_IRUGO; default: return 0;
}
}
/* device init is called only after the first addr resolution */
mutex_init(&ig.device_list_mutex);
INIT_LIST_HEAD(&ig.device_list);
mutex_init(&ig.connlist_mutex);
INIT_LIST_HEAD(&ig.connlist);
release_wq = alloc_workqueue("release workqueue", 0, 0); if (!release_wq) {
iser_err("failed to allocate release workqueue\n");
err = -ENOMEM; goto err_alloc_wq;
}
if (!connlist_empty) {
iser_err("Error cleanup stage completed but we still have iser " "connections, destroying them anyway\n");
list_for_each_entry_safe(iser_conn, n, &ig.connlist,
conn_list) {
iser_conn_release(iser_conn);
}
}
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.