/* * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved. * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved. * Copyright (c) 2013-2014 Mellanox Technologies. All rights reserved. * * 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.
*/ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/delay.h>
pd_err:
iser_err("failed to allocate an IB resource\n"); return -1;
}
/* * iser_free_device_ib_res - destroy/dealloc/dereg the DMA MR, * CQ and PD created with the device associated with the adaptor.
*/ staticvoid iser_free_device_ib_res(struct iser_device *device)
{
ib_unregister_event_handler(&device->event_handler);
ib_dealloc_pd(device->pd);
/** * iser_alloc_fastreg_pool - Creates pool of fast_reg descriptors * for fast registration work requests. * @ib_conn: connection RDMA resources * @cmds_max: max number of SCSI commands for this connection * @size: max number of pages per map request * * Return: 0 on success, or errno code on failure
*/ int iser_alloc_fastreg_pool(struct ib_conn *ib_conn, unsigned cmds_max, unsignedint size)
{ struct iser_device *device = ib_conn->device; struct iser_fr_pool *fr_pool = &ib_conn->fr_pool; struct iser_fr_desc *desc; int i, ret;
INIT_LIST_HEAD(&fr_pool->list);
INIT_LIST_HEAD(&fr_pool->all_list);
spin_lock_init(&fr_pool->lock);
fr_pool->size = 0; for (i = 0; i < cmds_max; i++) {
desc = iser_create_fastreg_desc(device, device->pd,
ib_conn->pi_support, size); if (IS_ERR(desc)) {
ret = PTR_ERR(desc); goto err;
}
out_err:
ib_cq_pool_put(ib_conn->cq, ib_conn->cq_size);
cq_err:
iser_err("unable to alloc mem or create resource, err %d\n", ret);
return ret;
}
/* * based on the resolved device node GUID see if there already allocated * device for this device. If there's no such, create one.
*/ static struct iser_device *iser_device_find_by_ib_device(struct rdma_cm_id *cma_id)
{ struct iser_device *device;
mutex_lock(&ig.device_list_mutex);
list_for_each_entry(device, &ig.device_list, ig_list) /* find if there's a match using the node GUID */ if (device->ib_device->node_guid == cma_id->device->node_guid) goto inc_refcnt;
device = kzalloc(sizeof *device, GFP_KERNEL); if (!device) goto out;
/* assign this device to the device */
device->ib_device = cma_id->device; /* init the device and link it into ig device list */ if (iser_create_device_ib_res(device)) {
kfree(device);
device = NULL; goto out;
}
list_add(&device->ig_list, &ig.device_list);
/* Wait for conn_stop to complete */
wait_for_completion(&iser_conn->stop_completion); /* Wait for IB resouces cleanup to complete */
wait_for_completion(&iser_conn->ib_completion);
/** * iser_free_ib_conn_res - release IB related resources * @iser_conn: iser connection struct * @destroy: indicator if we need to try to release the * iser device and memory regoins pool (only iscsi * shutdown and DEVICE_REMOVAL will use this). * * This routine is called with the iser state mutex held * so the cm_id removal is out of here. It is Safe to * be invoked multiple times.
*/ staticvoid iser_free_ib_conn_res(struct iser_conn *iser_conn, bool destroy)
{ struct ib_conn *ib_conn = &iser_conn->ib_conn; struct iser_device *device = ib_conn->device;
mutex_lock(&iser_conn->state_mutex); /* In case we endup here without ep_disconnect being invoked. */ if (iser_conn->state != ISER_CONN_DOWN) {
iser_warn("iser conn %p state %d, expected state down.\n",
iser_conn, iser_conn->state);
iscsi_destroy_endpoint(iser_conn->ep);
iser_conn->state = ISER_CONN_DOWN;
} /* * In case we never got to bind stage, we still need to * release IB resources (which is safe to call more than once).
*/
iser_free_ib_conn_res(iser_conn, true);
mutex_unlock(&iser_conn->state_mutex);
if (ib_conn->cma_id) {
rdma_destroy_id(ib_conn->cma_id);
ib_conn->cma_id = NULL;
}
kfree(iser_conn);
}
/** * iser_conn_terminate - triggers start of the disconnect procedures and * waits for them to be done * @iser_conn: iSER connection context * * Called with state mutex held
*/ int iser_conn_terminate(struct iser_conn *iser_conn)
{ struct ib_conn *ib_conn = &iser_conn->ib_conn; int err = 0;
lockdep_assert_held(&iser_conn->state_mutex);
/* terminate the iser conn only if the conn state is UP */ if (iser_conn->state != ISER_CONN_UP) return 0;
iser_conn->state = ISER_CONN_TERMINATING;
iser_info("iser_conn %p state %d\n", iser_conn, iser_conn->state);
/* suspend queuing of new iscsi commands */ if (iser_conn->iscsi_conn)
iscsi_suspend_queue(iser_conn->iscsi_conn);
/* * In case we didn't already clean up the cma_id (peer initiated * a disconnection), we need to Cause the CMA to change the QP * state to ERROR.
*/ if (ib_conn->cma_id) {
err = rdma_disconnect(ib_conn->cma_id); if (err)
iser_err("Failed to disconnect, conn: 0x%p err %d\n",
iser_conn, err);
/* block until all flush errors are consumed */
ib_drain_qp(ib_conn->qp);
}
return 1;
}
/* * Called with state mutex held
*/ staticvoid iser_connect_error(struct rdma_cm_id *cma_id)
{ struct iser_conn *iser_conn = cma_id->context;
/* * FRs without SG_GAPS can only map up to a (device) page per entry, * but if the first entry is misaligned we'll end up using two entries * (head and tail) for a single page worth data, so one additional * entry is required.
*/ if (attr->kernel_cap_flags & IBK_SG_GAPS_REG)
reserved_mr_pages = 0; else
reserved_mr_pages = 1;
if (iser_conn->ib_conn.pi_support)
max_num_sg = attr->max_pi_fast_reg_page_list_len; else
max_num_sg = attr->max_fast_reg_page_list_len;
/* * Called with state mutex held
*/ staticvoid iser_cleanup_handler(struct rdma_cm_id *cma_id, bool destroy)
{ struct iser_conn *iser_conn = cma_id->context;
lockdep_assert_held(&iser_conn->state_mutex); /* * We are not guaranteed that we visited disconnected_handler * by now, call it here to be safe that we handle CM drep * and flush errors.
*/ if (iser_conn_terminate(iser_conn)) { if (iser_conn->iscsi_conn)
iscsi_conn_failure(iser_conn->iscsi_conn,
ISCSI_ERR_CONN_FAILED); else
iser_err("iscsi_iser connection isn't bound\n");
}
iser_free_ib_conn_res(iser_conn, destroy);
complete(&iser_conn->ib_completion);
}
staticint iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
{ struct iser_conn *iser_conn; int ret = 0;
iser_conn = cma_id->context;
iser_info("%s (%d): status %d conn %p id %p\n",
rdma_event_msg(event->event), event->event,
event->status, cma_id->context, cma_id);
mutex_lock(&iser_conn->state_mutex); switch (event->event) { case RDMA_CM_EVENT_ADDR_RESOLVED:
iser_addr_handler(cma_id); break; case RDMA_CM_EVENT_ROUTE_RESOLVED:
iser_route_handler(cma_id); break; case RDMA_CM_EVENT_ESTABLISHED:
iser_connected_handler(cma_id, event->param.conn.private_data); break; case RDMA_CM_EVENT_REJECTED:
iser_info("Connection rejected: %s\n",
rdma_reject_msg(cma_id, event->status));
fallthrough; case RDMA_CM_EVENT_ADDR_ERROR: case RDMA_CM_EVENT_ROUTE_ERROR: case RDMA_CM_EVENT_CONNECT_ERROR: case RDMA_CM_EVENT_UNREACHABLE:
iser_connect_error(cma_id); break; case RDMA_CM_EVENT_DISCONNECTED: case RDMA_CM_EVENT_ADDR_CHANGE: case RDMA_CM_EVENT_TIMEWAIT_EXIT:
iser_cleanup_handler(cma_id, false); break; case RDMA_CM_EVENT_DEVICE_REMOVAL: /* * we *must* destroy the device as we cannot rely * on iscsid to be around to initiate error handling. * also if we are not in state DOWN implicitly destroy * the cma_id.
*/
iser_cleanup_handler(cma_id, true); if (iser_conn->state != ISER_CONN_DOWN) {
iser_conn->ib_conn.cma_id = NULL;
ret = 1;
} break; default:
iser_err("Unexpected RDMA CM event: %s (%d)\n",
rdma_event_msg(event->event), event->event); break;
}
mutex_unlock(&iser_conn->state_mutex);
/* * starts the process of connecting to the target * sleeps until the connection is established or rejected
*/ int iser_connect(struct iser_conn *iser_conn, struct sockaddr *src_addr, struct sockaddr *dst_addr, int non_blocking)
{ struct ib_conn *ib_conn = &iser_conn->ib_conn; int err = 0;
mutex_lock(&iser_conn->state_mutex);
sprintf(iser_conn->name, "%pISp", dst_addr);
iser_info("connecting to: %s\n", iser_conn->name);
/* the device is known only --after-- address resolution */
ib_conn->device = NULL;
if (desc && desc->sig_protected) {
desc->sig_protected = false;
ret = ib_check_mr_status(desc->rsc.sig_mr,
IB_MR_CHECK_SIG_STATUS, &mr_status); if (ret) {
iser_err("ib_check_mr_status failed, ret %d\n", ret); /* Not a lot we can do, return ambiguous guard error */
*sector = 0; return 0x1;
}
if (mr_status.fail_status & IB_MR_CHECK_SIG_STATUS) {
sector_t sector_off = mr_status.sig_err.sig_err_offset;
iser_err("PI error found type %d at sector %llx " "expected %x vs actual %x\n",
mr_status.sig_err.err_type,
(unsignedlonglong)*sector,
mr_status.sig_err.expected,
mr_status.sig_err.actual);
switch (mr_status.sig_err.err_type) { case IB_SIG_BAD_GUARD: return 0x1; case IB_SIG_BAD_REFTAG: return 0x3; case IB_SIG_BAD_APPTAG: return 0x2;
}
}
}
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.