📄 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 <asm/atomic.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_dbg.h>#include <scsi/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 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 const u8 topspin_oui[3] = { 0x00, 0x05, 0xad };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 ib_client srp_client = { .name = "srp", .add = srp_add_one, .remove = srp_remove_one};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 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 = dma_map_single(host->dev->dma_device, iu->buf, size, direction); if (dma_mapping_error(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; dma_unmap_single(host->dev->dma_device, 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, 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, srp_completion, NULL, target, SRP_CQ_SIZE); 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->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(target->srp_host->dev, target->srp_host->port, &target->path, 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); memcpy(req->priv.initiator_port_id, target->srp_host->initiator_port_id, 16); /* * Topspin/Cisco SRP targets will reject our login unless we * zero out the first 8 bytes of our initiator port ID. The * second 8 bytes must be our local node GUID, but we always * use that anyway. */ if (topspin_workarounds && !memcmp(&target->ioc_guid, topspin_oui, 3)) { 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.target_port_id, &target->id_ext, 8); memcpy(req->priv.target_port_id + 8, &target->ioc_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); ib_send_cm_dreq(target->cm_id, NULL, 0); wait_for_completion(&target->done);}static void srp_remove_work(void *target_ptr){ struct srp_target_port *target = target_ptr; spin_lock_irq(target->scsi_host->host_lock); if (target->state != SRP_TARGET_DEAD) { spin_unlock_irq(target->scsi_host->host_lock); scsi_host_put(target->scsi_host); return; } target->state = SRP_TARGET_REMOVED; spin_unlock_irq(target->scsi_host->host_lock); down(&target->srp_host->target_mutex); list_del(&target->list); up(&target->srp_host->target_mutex); scsi_remove_host(target->scsi_host); ib_destroy_cm_id(target->cm_id); srp_free_target_ib(target); scsi_host_put(target->scsi_host); /* And another put to really free the target port... */ 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 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; struct ib_wc wc; int ret; int i; 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, 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); target->cm_id = new_cm_id; qp_attr.qp_state = IB_QPS_RESET; ret = ib_modify_qp(target->qp, &qp_attr, IB_QP_STATE); if (ret) goto err; ret = srp_init_qp(target, target->qp); if (ret) goto err; while (ib_poll_cq(target->cq, 1, &wc) > 0) ; /* nothing */ list_for_each_entry(req, &target->req_queue, list) { req->scmnd->result = DID_RESET << 16; req->scmnd->scsi_done(req->scmnd); } target->rx_head = 0; target->tx_head = 0; target->tx_tail = 0; target->req_head = 0; for (i = 0; i < SRP_SQ_SIZE - 1; ++i) target->req_ring[i].next = i + 1; target->req_ring[SRP_SQ_SIZE - 1].next = -1; INIT_LIST_HEAD(&target->req_queue); ret = srp_connect_target(target); if (ret) goto err; spin_lock_irq(target->scsi_host->host_lock); if (target->state == SRP_TARGET_CONNECTING) { ret = 0; target->state = SRP_TARGET_LIVE; } else ret = -EAGAIN; spin_unlock_irq(target->scsi_host->host_lock); return ret;err: printk(KERN_ERR PFX "reconnect failed (%d), removing target port.\n", ret); /* * We couldn't reconnect, so kill our target port off. * However, we have to defer the real removal because we might * be in the context of the SCSI error handler now, which * would deadlock if we call scsi_remove_host(). */ spin_lock_irq(target->scsi_host->host_lock); if (target->state == SRP_TARGET_CONNECTING) { target->state = SRP_TARGET_DEAD; INIT_WORK(&target->work, srp_remove_work, target); schedule_work(&target->work); } spin_unlock_irq(target->scsi_host->host_lock); return ret;}static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, struct srp_request *req){ struct srp_cmd *cmd = req->cmd->buf; int len; u8 fmt; if (!scmnd->request_buffer || scmnd->sc_data_direction == DMA_NONE) return sizeof (struct srp_cmd); if (scmnd->sc_data_direction != DMA_FROM_DEVICE && scmnd->sc_data_direction != DMA_TO_DEVICE) { printk(KERN_WARNING PFX "Unhandled data direction %d\n", scmnd->sc_data_direction); return -EINVAL; } if (scmnd->use_sg) { struct scatterlist *scat = scmnd->request_buffer; int n; int i; n = dma_map_sg(target->srp_host->dev->dma_device, scat, scmnd->use_sg, scmnd->sc_data_direction); if (n == 1) { struct srp_direct_buf *buf = (void *) cmd->add_data; fmt = SRP_DATA_DESC_DIRECT; buf->va = cpu_to_be64(sg_dma_address(scat)); buf->key = cpu_to_be32(target->srp_host->mr->rkey); buf->len = cpu_to_be32(sg_dma_len(scat)); len = sizeof (struct srp_cmd) + sizeof (struct srp_direct_buf); } else { struct srp_indirect_buf *buf = (void *) cmd->add_data; u32 datalen = 0; fmt = SRP_DATA_DESC_INDIRECT; if (scmnd->sc_data_direction == DMA_TO_DEVICE) cmd->data_out_desc_cnt = n; else cmd->data_in_desc_cnt = n; buf->table_desc.va = cpu_to_be64(req->cmd->dma + sizeof *cmd + sizeof *buf); buf->table_desc.key = cpu_to_be32(target->srp_host->mr->rkey); buf->table_desc.len = cpu_to_be32(n * sizeof (struct srp_direct_buf)); for (i = 0; i < n; ++i) { buf->desc_list[i].va = cpu_to_be64(sg_dma_address(&scat[i])); buf->desc_list[i].key = cpu_to_be32(target->srp_host->mr->rkey); buf->desc_list[i].len = cpu_to_be32(sg_dma_len(&scat[i])); datalen += sg_dma_len(&scat[i]); } buf->len = cpu_to_be32(datalen); len = sizeof (struct srp_cmd) + sizeof (struct srp_indirect_buf) +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -