📄 ib_srp.c
字号:
/* * Copyright (c) 2005 Cisco Systems. 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. * * $Id: ib_srp.c 3932 2005-11-01 17:19:29Z roland $ */#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/err.h>#include <linux/string.h>#include <linux/parser.h>#include <linux/random.h>#include <linux/jiffies.h>#include <asm/atomic.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_dbg.h>#include <scsi/srp.h>#include <scsi/scsi_transport_srp.h>#include <rdma/ib_cache.h>#include "ib_srp.h"#define DRV_NAME "ib_srp"#define PFX DRV_NAME ": "#define DRV_VERSION "0.2"#define DRV_RELDATE "November 1, 2005"MODULE_AUTHOR("Roland Dreier");MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator " "v" DRV_VERSION " (" DRV_RELDATE ")");MODULE_LICENSE("Dual BSD/GPL");static int srp_sg_tablesize = SRP_DEF_SG_TABLESIZE;static int srp_max_iu_len;module_param(srp_sg_tablesize, int, 0444);MODULE_PARM_DESC(srp_sg_tablesize, "Max number of gather/scatter entries per I/O (default is 12)");static int topspin_workarounds = 1;module_param(topspin_workarounds, int, 0444);MODULE_PARM_DESC(topspin_workarounds, "Enable workarounds for Topspin/Cisco SRP target bugs if != 0");static int mellanox_workarounds = 1;module_param(mellanox_workarounds, int, 0444);MODULE_PARM_DESC(mellanox_workarounds, "Enable workarounds for Mellanox SRP target bugs if != 0");static void srp_add_one(struct ib_device *device);static void srp_remove_one(struct ib_device *device);static void srp_completion(struct ib_cq *cq, void *target_ptr);static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);static struct scsi_transport_template *ib_srp_transport_template;static struct ib_client srp_client = { .name = "srp", .add = srp_add_one, .remove = srp_remove_one};static struct ib_sa_client srp_sa_client;static inline struct srp_target_port *host_to_target(struct Scsi_Host *host){ return (struct srp_target_port *) host->hostdata;}static const char *srp_target_info(struct Scsi_Host *host){ return host_to_target(host)->target_name;}static int srp_target_is_topspin(struct srp_target_port *target){ static const u8 topspin_oui[3] = { 0x00, 0x05, 0xad }; static const u8 cisco_oui[3] = { 0x00, 0x1b, 0x0d }; return topspin_workarounds && (!memcmp(&target->ioc_guid, topspin_oui, sizeof topspin_oui) || !memcmp(&target->ioc_guid, cisco_oui, sizeof cisco_oui));}static int srp_target_is_mellanox(struct srp_target_port *target){ static const u8 mellanox_oui[3] = { 0x00, 0x02, 0xc9 }; return mellanox_workarounds && !memcmp(&target->ioc_guid, mellanox_oui, sizeof mellanox_oui);}static struct srp_iu *srp_alloc_iu(struct srp_host *host, size_t size, gfp_t gfp_mask, enum dma_data_direction direction){ struct srp_iu *iu; iu = kmalloc(sizeof *iu, gfp_mask); if (!iu) goto out; iu->buf = kzalloc(size, gfp_mask); if (!iu->buf) goto out_free_iu; iu->dma = ib_dma_map_single(host->dev->dev, iu->buf, size, direction); if (ib_dma_mapping_error(host->dev->dev, iu->dma)) goto out_free_buf; iu->size = size; iu->direction = direction; return iu;out_free_buf: kfree(iu->buf);out_free_iu: kfree(iu);out: return NULL;}static void srp_free_iu(struct srp_host *host, struct srp_iu *iu){ if (!iu) return; ib_dma_unmap_single(host->dev->dev, iu->dma, iu->size, iu->direction); kfree(iu->buf); kfree(iu);}static void srp_qp_event(struct ib_event *event, void *context){ printk(KERN_ERR PFX "QP event %d\n", event->event);}static int srp_init_qp(struct srp_target_port *target, struct ib_qp *qp){ struct ib_qp_attr *attr; int ret; attr = kmalloc(sizeof *attr, GFP_KERNEL); if (!attr) return -ENOMEM; ret = ib_find_cached_pkey(target->srp_host->dev->dev, target->srp_host->port, be16_to_cpu(target->path.pkey), &attr->pkey_index); if (ret) goto out; attr->qp_state = IB_QPS_INIT; attr->qp_access_flags = (IB_ACCESS_REMOTE_READ | IB_ACCESS_REMOTE_WRITE); attr->port_num = target->srp_host->port; ret = ib_modify_qp(qp, attr, IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_ACCESS_FLAGS | IB_QP_PORT);out: kfree(attr); return ret;}static int srp_create_target_ib(struct srp_target_port *target){ struct ib_qp_init_attr *init_attr; int ret; init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL); if (!init_attr) return -ENOMEM; target->cq = ib_create_cq(target->srp_host->dev->dev, srp_completion, NULL, target, SRP_CQ_SIZE, 0); if (IS_ERR(target->cq)) { ret = PTR_ERR(target->cq); goto out; } ib_req_notify_cq(target->cq, IB_CQ_NEXT_COMP); init_attr->event_handler = srp_qp_event; init_attr->cap.max_send_wr = SRP_SQ_SIZE; init_attr->cap.max_recv_wr = SRP_RQ_SIZE; init_attr->cap.max_recv_sge = 1; init_attr->cap.max_send_sge = 1; init_attr->sq_sig_type = IB_SIGNAL_ALL_WR; init_attr->qp_type = IB_QPT_RC; init_attr->send_cq = target->cq; init_attr->recv_cq = target->cq; target->qp = ib_create_qp(target->srp_host->dev->pd, init_attr); if (IS_ERR(target->qp)) { ret = PTR_ERR(target->qp); ib_destroy_cq(target->cq); goto out; } ret = srp_init_qp(target, target->qp); if (ret) { ib_destroy_qp(target->qp); ib_destroy_cq(target->cq); goto out; }out: kfree(init_attr); return ret;}static void srp_free_target_ib(struct srp_target_port *target){ int i; ib_destroy_qp(target->qp); ib_destroy_cq(target->cq); for (i = 0; i < SRP_RQ_SIZE; ++i) srp_free_iu(target->srp_host, target->rx_ring[i]); for (i = 0; i < SRP_SQ_SIZE + 1; ++i) srp_free_iu(target->srp_host, target->tx_ring[i]);}static void srp_path_rec_completion(int status, struct ib_sa_path_rec *pathrec, void *target_ptr){ struct srp_target_port *target = target_ptr; target->status = status; if (status) printk(KERN_ERR PFX "Got failed path rec status %d\n", status); else target->path = *pathrec; complete(&target->done);}static int srp_lookup_path(struct srp_target_port *target){ target->path.numb_path = 1; init_completion(&target->done); target->path_query_id = ib_sa_path_rec_get(&srp_sa_client, target->srp_host->dev->dev, target->srp_host->port, &target->path, IB_SA_PATH_REC_SERVICE_ID | IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID | IB_SA_PATH_REC_NUMB_PATH | IB_SA_PATH_REC_PKEY, SRP_PATH_REC_TIMEOUT_MS, GFP_KERNEL, srp_path_rec_completion, target, &target->path_query); if (target->path_query_id < 0) return target->path_query_id; wait_for_completion(&target->done); if (target->status < 0) printk(KERN_WARNING PFX "Path record query failed\n"); return target->status;}static int srp_send_req(struct srp_target_port *target){ struct { struct ib_cm_req_param param; struct srp_login_req priv; } *req = NULL; int status; req = kzalloc(sizeof *req, GFP_KERNEL); if (!req) return -ENOMEM; req->param.primary_path = &target->path; req->param.alternate_path = NULL; req->param.service_id = target->service_id; req->param.qp_num = target->qp->qp_num; req->param.qp_type = target->qp->qp_type; req->param.private_data = &req->priv; req->param.private_data_len = sizeof req->priv; req->param.flow_control = 1; get_random_bytes(&req->param.starting_psn, 4); req->param.starting_psn &= 0xffffff; /* * Pick some arbitrary defaults here; we could make these * module parameters if anyone cared about setting them. */ req->param.responder_resources = 4; req->param.remote_cm_response_timeout = 20; req->param.local_cm_response_timeout = 20; req->param.retry_count = 7; req->param.rnr_retry_count = 7; req->param.max_cm_retries = 15; req->priv.opcode = SRP_LOGIN_REQ; req->priv.tag = 0; req->priv.req_it_iu_len = cpu_to_be32(srp_max_iu_len); req->priv.req_buf_fmt = cpu_to_be16(SRP_BUF_FORMAT_DIRECT | SRP_BUF_FORMAT_INDIRECT); /* * In the published SRP specification (draft rev. 16a), the * port identifier format is 8 bytes of ID extension followed * by 8 bytes of GUID. Older drafts put the two halves in the * opposite order, so that the GUID comes first. * * Targets conforming to these obsolete drafts can be * recognized by the I/O Class they report. */ if (target->io_class == SRP_REV10_IB_IO_CLASS) { memcpy(req->priv.initiator_port_id, &target->path.sgid.global.interface_id, 8); memcpy(req->priv.initiator_port_id + 8, &target->initiator_ext, 8); memcpy(req->priv.target_port_id, &target->ioc_guid, 8); memcpy(req->priv.target_port_id + 8, &target->id_ext, 8); } else { memcpy(req->priv.initiator_port_id, &target->initiator_ext, 8); memcpy(req->priv.initiator_port_id + 8, &target->path.sgid.global.interface_id, 8); memcpy(req->priv.target_port_id, &target->id_ext, 8); memcpy(req->priv.target_port_id + 8, &target->ioc_guid, 8); } /* * Topspin/Cisco SRP targets will reject our login unless we * zero out the first 8 bytes of our initiator port ID and set * the second 8 bytes to the local node GUID. */ if (srp_target_is_topspin(target)) { printk(KERN_DEBUG PFX "Topspin/Cisco initiator port ID workaround " "activated for target GUID %016llx\n", (unsigned long long) be64_to_cpu(target->ioc_guid)); memset(req->priv.initiator_port_id, 0, 8); memcpy(req->priv.initiator_port_id + 8, &target->srp_host->dev->dev->node_guid, 8); } status = ib_send_cm_req(target->cm_id, &req->param); kfree(req); return status;}static void srp_disconnect_target(struct srp_target_port *target){ /* XXX should send SRP_I_LOGOUT request */ init_completion(&target->done); if (ib_send_cm_dreq(target->cm_id, NULL, 0)) { printk(KERN_DEBUG PFX "Sending CM DREQ failed\n"); return; } wait_for_completion(&target->done);}static void srp_remove_work(struct work_struct *work){ struct srp_target_port *target = container_of(work, struct srp_target_port, work); spin_lock_irq(target->scsi_host->host_lock); if (target->state != SRP_TARGET_DEAD) { spin_unlock_irq(target->scsi_host->host_lock); return; } target->state = SRP_TARGET_REMOVED; spin_unlock_irq(target->scsi_host->host_lock); spin_lock(&target->srp_host->target_lock); list_del(&target->list); spin_unlock(&target->srp_host->target_lock); srp_remove_host(target->scsi_host); scsi_remove_host(target->scsi_host); ib_destroy_cm_id(target->cm_id); srp_free_target_ib(target); scsi_host_put(target->scsi_host);}static int srp_connect_target(struct srp_target_port *target){ int ret; ret = srp_lookup_path(target); if (ret) return ret; while (1) { init_completion(&target->done); ret = srp_send_req(target); if (ret) return ret; wait_for_completion(&target->done); /* * The CM event handling code will set status to * SRP_PORT_REDIRECT if we get a port redirect REJ * back, or SRP_DLID_REDIRECT if we get a lid/qp * redirect REJ back. */ switch (target->status) { case 0: return 0; case SRP_PORT_REDIRECT: ret = srp_lookup_path(target); if (ret) return ret; break; case SRP_DLID_REDIRECT: break; default: return target->status; } }}static void srp_unmap_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, struct srp_request *req){ if (!scsi_sglist(scmnd) || (scmnd->sc_data_direction != DMA_TO_DEVICE && scmnd->sc_data_direction != DMA_FROM_DEVICE)) return; if (req->fmr) { ib_fmr_pool_unmap(req->fmr); req->fmr = NULL; } ib_dma_unmap_sg(target->srp_host->dev->dev, scsi_sglist(scmnd), scsi_sg_count(scmnd), scmnd->sc_data_direction);}static void srp_remove_req(struct srp_target_port *target, struct srp_request *req){ srp_unmap_data(req->scmnd, target, req); list_move_tail(&req->list, &target->free_reqs);}static void srp_reset_req(struct srp_target_port *target, struct srp_request *req){ req->scmnd->result = DID_RESET << 16; req->scmnd->scsi_done(req->scmnd); srp_remove_req(target, req);}static int srp_reconnect_target(struct srp_target_port *target){ struct ib_cm_id *new_cm_id; struct ib_qp_attr qp_attr; struct srp_request *req, *tmp; struct ib_wc wc; int ret; spin_lock_irq(target->scsi_host->host_lock); if (target->state != SRP_TARGET_LIVE) { spin_unlock_irq(target->scsi_host->host_lock); return -EAGAIN; } target->state = SRP_TARGET_CONNECTING; spin_unlock_irq(target->scsi_host->host_lock); srp_disconnect_target(target); /* * Now get a new local CM ID so that we avoid confusing the * target in case things are really fouled up. */ new_cm_id = ib_create_cm_id(target->srp_host->dev->dev, srp_cm_handler, target); if (IS_ERR(new_cm_id)) { ret = PTR_ERR(new_cm_id); goto err; } ib_destroy_cm_id(target->cm_id);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -